简体   繁体   中英

Referring abstract type member of a type parameter

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 dB . You have a type mismatch because in foo , B actually meant this.B , which as said is not the same as D#B (the latter being more general). Informally, you can think of D#B as representing any possible type that the abstract type B can take for any instance of D , while dB 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 dB :

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!
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM