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.
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.
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 multipleLet'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[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.
def foo[C <: Traversable[_]](c : C) = new {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 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
}
}
// We create a subclass where the type parameter to Traversable is *different* from the subclass.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:
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)
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.