Reputation: 2387
I came across this post on the scala forum.
Here is a recap:
I just realized that the Scala compiler does not seem to "carry over" type boundaries on wildcards:
scala> class Foo { def foo = 42 }
defined class Foo
scala> class Bar[A <: Foo](val a: A)
defined class Bar
scala> def bar(x: Bar[_]) = x.a.foo
:7: error: value foo is not a member of Any
I would expect that the parameter of method bar is still upper-bound by Foo, even though its exact type parameter is unimportant in the method. Is there a specific reason for this behavior?
The discussion then goes into a Spec interpretation dispute :)
Eventually the poster suggest this explanation:
However, if the compiler did this for Bar[_], for consistency reasons it would also have to do it for Bar[A]. The latter however would have some strange consequences. def bar[A](x: Bar[A], y: Bob[A]) for example would suddenly have to carry an implicit type bound for Bob. If Bob had its own type bound things would be really messy.
Which I don't get because
def bar[A](x: Bar[A])
alone wouldn't compile since Bar type parameter is bounded.
Anyway I believe the following would make total sense:
def bar(x: Bar[_], y : Bob[_])
As a workaround they suggest:
def bar(x: Bar[_ <: Foo]) = x.a.foo
Which beside not being DRY it makes things difficult:
Let's consider a Tree
abstract class Tree[T <: Tree[T]] { val subTree : List[T] }
Now how would you define functions (defined outside of the Tree class obviously) that are going through the tree recursively:
def size( tree : Tree[_] ) = tree.subTree.size + tree.subTree.map(size(_)).sum
obviously won't work, since subTree will be turned into a List[Any], so we need a type parameter:
def size[T <: Tree[T]]( tree : T ) = ...
Even uglier :
class OwnATree( val myTree : Tree[_] ){}
Should become
class OwnATree[T <: Tree[T]]( val myTree : T ){}
etc, etc ...
I believe there's something wrong somewhere :)
Upvotes: 5
Views: 422
Reputation: 139038
The simplest way to accomplish what you want with size
and OwnATree
is to use an existential type:
def size(tree: Tree[T] forSome { type T <: Tree[T] }): Int =
tree.subTree.size + tree.subTree.map(size(_)).sum
And:
class OwnATree(val myTree: Tree[T] forSome { type T <: Tree[T] })
Your Tree[_]
versions are actually also using existential types—they're just wrapped up in some syntactic sugar. In other words,
def size(tree: Tree[_]): Int = ...
Is just syntactic sugar for:
def size(tree: Tree[T] forSome { type T }): Int = ...
You can't add the constraint you need to the underscore version, but you can to the desugared one.
Upvotes: 5