3

I'm currently working on a A* implementation in Scala. To accomplish a clean structure I would like to use a nested case class structure which implements a self-bounded trait. However, I experience some issues when implementing this in the Scala IDE. The following code will not compile:

trait A[T <: A[T]]

class B {

    case class C(int: Int) extends A[C] // A[this.C] won't work either

    def make = C(5)

}

object D {

    def foo[T <: A[T]](some: T) = {}

    val c = new B().make

    foo(c) // this does not compile

}

Is there any way I can make this structure work?

Y4sper
  • 107
  • 1
  • 9
  • A nice explanation of path-dependent types can be found at http://stackoverflow.com/questions/2693067/what-is-meant-by-scalas-path-dependent-types – manub Jan 16 '16 at 18:10

1 Answers1

5

Not sure why you want this, but here's why it won't work as is:

The type of D.c is B#C. It is a path-dependent type where we don't know what instance of B it belongs to. However, C extends A[C], which is the already the same as saying A[this.C] in that context, which is bound to a specific instance of B. foo sees the type parameter T as B#C, which is not the same as b.C for some b.

You have two options to make this compile.

Relax the constraints of A to B#C:

trait A[T <: A[T]]

class B {

    case class C(int: Int) extends A[B#C]

    def make = C(5)

}

object D {

    def foo[T <: A[T]](some: A[T]) = {}

    val c = new B().make

    foo(c)

}

Or handle the path-dependent type, so that c has type b.C:

trait A[T <: A[T]]

class B {

    case class C(int: Int) extends A[C]

    def make = C(5)

}

object D {

    def foo[T <: A[T]](some: A[T]) = {}

    val b = new B
    val c: b.C = b.make

    foo(c)

}
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • Thanks! This explained a lot! – Y4sper Jan 16 '16 at 19:37
  • The explicit type on `c` in the last example isn't actually necessary, I found. I would love an explanation for why introducing the intermediate val `b` changes the inferred type of `c`. I get that there is no name for the intermediate value without `b` but not how that matters to the inference. – Joe Pallas Jan 18 '16 at 01:03
  • I don't think there is a spec for type inference rules, so I'm at a loss there. I just don't think the type inferencer is smart enough in this case to do it all in one statement--similar to why you need to curry arguments like `def f(b: B)(c: b.C)` – Michael Zajac Jan 18 '16 at 01:11