3

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 ?

Eduardo Pareja Tobes
  • 2,850
  • 1
  • 16
  • 18
Chikei
  • 2,064
  • 1
  • 17
  • 20

2 Answers2

3

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)
  }
}
Community
  • 1
  • 1
Régis Jean-Gilles
  • 31,374
  • 4
  • 75
  • 92
1

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!
  }
}
Community
  • 1
  • 1
Eduardo Pareja Tobes
  • 2,850
  • 1
  • 16
  • 18