Quantcast
Viewing all articles
Browse latest Browse all 25

Preserving Types and Differing Subclass Type Parameters

My last post was on keeping your type annotations as simple as possible to solve your problem. It is important to keep your type system h4x0r-y as simple as possible to solve the given problem, but no simpler. This gives users the greatest flexibility. So, when doing some kind of query from a Traversable type that does *not* return the original type, you can simply take type Traversable[T], where T is a parameter to your function as well.

I started off the last post attempting to solve a problem I did not have. However, what if I did have this problem? That is: I want to preserve the type of a subclass of traversable for the return of my function. The subclass may have a differing type parameter then the traversable, or in my contrived example: TestClass[X] extends Traversable[Y]. Well, let's start digging into a solution.

Let's ignore things that did *not* work in the last post.

def foo[A, M[T] <: Traversable[T]](m : M[A]) = ... // M has differing type parameter, or multiple
def foo[A, M[_] <: Traversable[_]](m : M[A]) = ... // A is not the parameter to Traversable..
def foo[A : Manifest, M <: Traversable[A]](m : M) = ... // Type Inference does not work, but this *does* abide by the type system.
Let's see if we can fix the type inference problem on the last option. To do so, we need to find a way to help the type system out (as it's not very fond of inferring types out of a subclass). Let's take a quick crack at this:
def foo[C <: Traversable[_]](c : C) = new {
// We now know the type of C from inference. Let's see if we can infer the type
// associated with the Traversable *correctly*
def apply[X : Manifest](t : Traversable[X]) = {
// We're going to print the inferred Traversable type to make sure it comes out correctly
println(implicitly[Manifest[X]])
// The resulting type should be the infered C
c
}
}
What we've done is created a curried function that *takes the same parameter twice*. The first time we infer it's full type C and ensure it's a subclass of Traversable. The second function (apply) will infer the type parameter to the Traversable subclass and reify it into a Manifest. We print this value to ensure we've done everything correctly. Now it's time to test and see if we've achieved our goal!
// We create a subclass where the type parameter to Traversable is *different* from the subclass.
class Temp[X] extends Traversable[Int] {
def foreach[U](f : Int => U) : Unit = f(5)
}

// This is now interpreter output
scala> val x = new Temp[String]
x: Temp[String] = line2(5)

scala> foo(x)(x)
Int
res0: Temp[String] = line2(5)
So we have the unfortunate ugliness of having to send the parameter to *two* function, since we split the type inference in half, However, notice that we've inferred the type parameter to Traversable correctly! Also notice that the return type is Temp[String], which is exactly what we desired. So, we had to jump a few hoops to get what we want. The unfortunate truth is that this kind of inference can't be done all in one go (And if you figure out a way, please let me know!!!). However, it's still possible to manipulate the type system to do what we want, albeit at the cost of some extra ugliness. Why might I want to perform this kind of trickery? Let's suppose Temp[String] is an immutable type that acts like a Traversable on Ints. I have a method that wants to iterate those Ints and create some kind of new Temp[String] based on what it found. To do this, I'm using a builder pattern with implicit builders. Let's take a look at the new signature I can use to accomplish my goal:
def foo[C <: Traversable[_] : Builder](c : C) = new {
def apply[X : Manifest](t : Traversable[X]) = {

// We're going to use the traversable, and we need the manifest of X to do some
// specialized calculations, maybe some Array fun.
val result = doSomethingWithTraversableUsingXManifest(t)

// Construct a new C using its Builder with a modified Attribute
implicitly[Builder[C]].copyWithModify(c, ModifyAttribute(MyAttribute, result))
}
}

As you can see, this is now possible! I'm making the Builder / Modify attribute magic purposefully hazy as I hope to cover this in a blog article in the near future.

Viewing all articles
Browse latest Browse all 25

Trending Articles