Reputation: 135
Consider the following code
trait Foo[T] {
def one: Foo[_ >: T]
def two: T
def three(x: T)
}
def test[T](f: Foo[T]) = {
val b = f.one
b.three(b.two)
}
The method test fails to type check. It says:
found : (some other)_$1(in value b)
required: _$1(in value b)
val x = b.three(b.two)
If I am interpreting this correctly, the compiler thinks that b in method test has a type that looks like this (not legal syntax, but hopefully clearer):
trait Foo {
def two: X where ∃ X >: T
def three(x: X where ∃ X >: T)
}
What I was hoping for was that it would have a type like this:
∃ X >: T such that trait Foo {
def two: X
def three(x: X)
}
The intent being that while the precise type X is not known, the compiler knows that its the same unknown type being returned by "two" and expected by "three". This seems different from what happens with normal univerally quantified generics. The following compiles, but exposes the type parameter X which I want to hide as it will vary between instances of Foo:
trait Foo[T] {
def one[X >: T]: Foo[X]
def two: T
def three(x: T)
}
def test[T, X >: T](f: Foo[T]) = {
val b = f.one[X]
b.three(b.two)
}
Is there a way to get the same behaviour for existentially quantified generics we get when they're univerally quanified?
Upvotes: 4
Views: 139
Reputation: 1927
The problem is the compiler thinks that
b.two: _>:T
b.three(_>:T)
i.e. two is a supertype of T and three requires a supertype of T. But a supertype of T is not necessarily assignment compatible with another supertype of T, as in this example:
A >: B >: C
def get:A
def put(B)
put(get) // type mismatch
So if all the information we have is that they are supertypes of T then we cannot do this safely. We have to explicitly tell the compiler that they are the same supertype of T.
trait Foo[T] {
type U <: T
def one: Foo[U]
def two: T
def three(x: T)
}
Then just set U
when you implement the trait:
val x = new Foo[Dog]{
type U = Mammal
...
I would prefer this approach over the existential types due to the cleaner syntax and the fact that this is core Scala and does not need the feature to be imported.
Upvotes: 2
Reputation: 11366
def one: Foo[_ >: T]
is equivalent to
def one: Foo[U >: T] forSome {type U >: T}
this one instead works
def one: Foo[U forSome {type U >: T}]
I do not however understand why this would make a difference. It seems like it should not to me. (shrug)
Upvotes: 2