Referring abstract type member of a type parameter - scala

My scenario is like:
trait A {
type B
def foo(b: B)
}
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
d.foo(createB)
}
}
In REPL, it complains
<console>:24: error: type mismatch;
found : D#B
required: C.this.d.B
a.bar(createB())
What's wrong with this ? And (if possible at all) how to correct this code ?

D#B is a type projection, and is not the same as d.B. You have a type mismatch because in foo, Bactually meant this.B, which as said is not the same as D#B (the latter being more general).
Informally, you can think of D#Bas representing any possible type that the abstract type B can take for any instance of D, while d.B is the type of B for the specific instance d.
See What does the `#` operator mean in Scala? and What is meant by Scala's path-dependent types? for some context.
One way to make it compile it is by changing createB's return type to d.B:
def createB(): d.B
However in many cases such a solution is too restrictive because you are tied to the specific instance d, which might not be what you had in mind.
Another solution is then to replace the abstract type with a type parameter (though it is more verbose):
trait A[B] {
def foo(b: B)
}
trait C[B, D <: A[B]] {
val d: D
def createB(): B
def bar() {
d.foo(createB)
}
}

Update given this answer I'm not sure whether this should be considered a bug or not
This is a bug: SI-4377. An explicit type ascription yields
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
(d:D).foo(createB)
// [error] found : D#B
// [error] required: _3.B where val _3: D
}
}
which looks like the implementation leaking. There's a workaround which involves casting to an intersection type (dangerous, casting is wrong etc; see my other answer here)
trait A {
type B
def foo(b: B)
}
case object A {
type is[A0 <: A] = A0 {
type B = A0#B
}
def is[A0 <: A](a: A0): is[A0] = a.asInstanceOf[is[A0]]
}
trait C[D <: A] {
val d: D
def createB(): D#B
def bar() {
A.is(d).foo(createB) // use it here!
}
}

Related

T <: A, return T method

here is some sample code:
trait A
trait B extends A
def test[T <: A](): T = {
new B {}
}
but I get an compile error:
type mismatch;
found : B
required: T
new B {}
how to make it working ?
( but without doing asInstanceOf[T] at the end )
thanks!
The signature of your method
def test[T <: A](): T
promises that for any type T that is a subtype of A you return a value of this type T. And then you returned a value of type B. You violated the signature (there are many subtypes of A, not only B).
For example you can try a type class (in this way you say that you return a value of T not for any T <: A at all but for any T <: A that can be handled by the type class MakeT)
def test[T <: A]()(implicit makeT: MakeT[T]): T = makeT.makeT()
trait MakeT[T] { // or MakeT[+T]
def makeT(): T
}
object MakeT {
implicit val bMakeT: MakeT[B] = () => new B {}
// or implicit def bMakeT[T >: B]: MakeT[T] = () => new B {}
}
test[B]().isInstanceOf[B] // true
In the situation described by #LuisMiguelMejíaSuárez in his comment
trait C extends A
you'll have
// test[C]() // doesn't compile, could not find implicit value for parameter makeT: MakeT[C]
Regarding generic return type see also
Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?
Type mismatch on abstract type used in pattern matching
Or you can use standard type classes =:=, <:<
def test[T <: A]()(implicit ev: B =:= T): T = {
new B {}
}
(not implicit ev: T =:= B)
or
def test[T <: A]()(implicit ev: B <:< T): T = {
new B {}
}

Why scala does not unify this type lambda with underlying type?

trait A {
type T
def test(t: T): Unit
}
case class B[S <: A](a: S, t : S#T) {
def test() = a.test(t) // Error: type mismatch;
// found : B.this.t.type (with underlying type S#T)
// required: B.this.a.T
}
Am I wrong to expect the above to compile? Can my code be fixed?
Compiler has not sufficient evidence that S#T can be used as argument for test in concrete instance.
Consider this hypotecical example for weakened scala compiler
trait A2 extends A{
type T <: AnyRef
}
class A3 extends A2{
override type T = Integer
def test(t: Integer): Unit = println(t * 2)
}
So B[A2] should accept instance of A3 along with anything that is <: AnyRef while A3 needs exactly Integer for its own test implementation
You can catch concrete type in the definition of B, to make sure what type will be used
case class B[S <: A, ST](a: S {type T = ST}, t: ST) {
def test() = a.test(t)
}
I could come up with encodings (removed the type parameters for simplification):
scala> :paste
// Entering paste mode (ctrl-D to finish)
def test0(a: A)(t : a.T) = a.test(t)
abstract class B{
val a: A
val t: a.T
def test = a.test(t)
}
// Exiting paste mode, now interpreting.
test0: (a: A)(t: a.T)Unit
defined class B
This on the other hand didn't work with case classes arguments (nor classes' for that matter).
One of the reasons your encoding wouldn't work:
scala> def test1(a: A)(t : A#T) = a.test(t)
<console>:12: error: type mismatch;
found : t.type (with underlying type A#T)
required: a.T
def test1(a: A)(t : A#T) = a.test(t)
The important part is required: a.T (versus A#T). The test method in A doesn't take any T, it takes T this.T, or in other words, the T belonging to one particular instance of A.
Instead of a type projection you can use the dependent type a.T:
trait A {
type T
def test(t: T): Unit
}
case class B[S <: A](a: S)(t : a.T) {
def test() = a.test(t)
}

Scala type inference for existential types and type members

The following piece of code does not compile :
trait A[F] {
def find(x: Int): F
def fill(f: F): Unit
}
object TestA {
def test[T <: A[F] forSome { type F }](t: T) =
t.fill(t.find(0))
}
It returns the following compilation error :
test.scala:8: error: type mismatch;
found : (some other)F(in type T)
required: F(in type T)
t.fill(t.find(0))
^
However the following code complies just fine :
trait B[F] {
type R = F
def find(x: Int): R
def fill(f: R): Unit
}
object TestB {
def test[T <: B[F] forSome { type F }](t: T) =
t.fill(t.find(0))
}
I have two questions here :
I expect the fist piece of code to compile. Why does it not?
If there is a good reason why first piece of code does not compile, I would expect the second to not compile either, for the same reason. How then, does it compile successfully?
Is either of these a bug?
I don't know why the compiler differentiates the two pieces of code. Basically, the code doesn't compile because the type returned by find and the type expected by fill don't have to be the same F, at least if find and fill were called on two different objects.
You could make the first piece of code to compile with:
def test[T <: A[F], F](t: T) =
t.fill(t.find(0))
And you could make the second piece of code not to compile with:
def test[T <: B[F] forSome { type F }](t: T, u: T) =
t.fill(u.find(0))
This should be rather a comment than an answer, but I don't have 50 reputation yet.
To understand what's happening, let's look at simpler versions of TestA.test and TestB.test.
object TestA {
def test1(a: A[String]) = {
val s: String = a.find(0)
a.fill(s)
}
}
object TestB {
def test1(b: B[String]) = {
val r: b.R = b.find(0)
b.fill(r)
}
}
Notice how the type of the intermediate value s refers to String, while the type of the intermediate value r does not.
object TestA {
def test2(a: A[F] forSome {type F}) = {
val s: F forSome {type F} = a.find(0)
// type mismatch;
// found : s.type (with underlying type F forSome { type F })
// required: F
a.fill(s)
}
}
object TestB {
def test2(b: B[F] forSome {type F}) = {
val r: b.R = b.find(0)
b.fill(r)
}
}
Once we throw in the existentials, we end up with an intermediate value s whose type is equivalent to Any, and which thus isn't a valid input for a.fill. The intermediate type for r, however, is still b.R, and so it is still an appropriate input for b.fill. The reason its type is still b.R is because b.R doesn't refer to F, and so according to the simplification rules for existential types, b.R forSome {type F} is equivalent to b.R, in the same way that Int forSome {type F} is equivalent to Int.
Is either of these a bug?
Well, there is certainly a bug somewhere (as of scalac 2.11.7), because the following does not type check.
object TestB {
def test3(b: B[F] forSome {type F}) = {
val r: b.R forSome {type F} = b.find(0)
// type mismatch;
// found : F
// required: b.R
// (which expands to) F
b.fill(r)
}
}
So either I'm wrong to think that b.R does not refer to F, in which case b.R forSome {type F} is not equivalent to b.R and your TestB.test should not type check but it does, or b.R forSome {type F} is equivalalent to b.R, in which case my TestB.test3 should type check but it doesn't.
I'm quite convinced that the bug is with the latter, because the error even occurs when the existential quantification has nothing to do with b.R, as in the following example.
object TestB {
def test4(b: B[F] forSome {type F}) = {
val r: b.R forSome {val x: Int} = b.find(0)
// type mismatch;
// found : F
// required: b.R
// (which expands to) F
b.fill(r)
}
}

Can an existentially quantified type variable be forced to have only a single type?

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?
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)
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.

Types in Scala - lower bounds

on code below.
My expectation is that T must be a of type B or A, so call to lowerBound(new D) should probably not compile (?). Similar experiments with upperbound give me expected typecheck errors.
Thanks for giving the hint.
object varianceCheck {
class A {
override def toString = this.getClass.getCanonicalName
}
class B extends A
class C extends B
class D extends C
def lowerBound[T >: B](param: T) = { param }
println(lowerBound(new D)) //> varianceCheck.D
}
With your implementation you can write:
scala> def lowerBound[T >: B](param: T) = { param }
lowerBound: [T >: B](param: T)T
scala> lowerBound(new AnyRef {})
res0: AnyRef = $anon$1#2eef224
where AnyRef is a super type of all object/reference types (actually it is an alias for Java Object class). And this is right, T >: B expresses that the type parameter T or the abstract type T refer to a supertype of type B.
You just have a bad example with toString, cause this method has all object types, but if you change it to, let's say on someMethod, your lowerBound won't compile:
<console>:18: error: value someMethod is not a member of type parameter T
def lowerBound[T >: B](param: T) = { param.someMethod }
If you change this to T <: B, which means that parameter of type T is a subclass of B, than everything is good, cause this param has someMethod method:
def lowerBound[T <: B](param: T) = { param.someMethod }
Faced same question as well. Seems compiler doing great job to help us shoot our feets. If you extract result of the lowerBound you may notice that it is of type B
val b: B = lowerBound(new D)
println(b) //> varianceCheck.D
And then if you try though to request type D explicitly
lowerBound[D](new D)
you will see the compiler error that you expected:
Error:(12, 21) type arguments [D] do not conform to method lowerBound's type parameter bounds [T >: B]
lowerBound[D](new D)