6
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?

JbGi
  • 197
  • 6

3 Answers3

8

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) 
}
Odomontois
  • 15,918
  • 2
  • 36
  • 71
  • 2
    But `B[A2](new A3(), "")` does not compile though... did you mean that it would with my code? Otherwise your solution is exactly what I needed. – JbGi Dec 05 '16 at 21:35
  • Ha, a clever trick! I didn't know one can use type parameter on the right hand sight in the refinement. – Haspemulator Dec 05 '16 at 21:36
  • @JbGi Yes I thought a lot how to demonstrate this with an actually compiling example, but scala compiler is pretty restrictive here. So let keep this just an imaginary example for imaginary scala-like compiler, that could compile original source – Odomontois Dec 06 '16 at 12:08
  • forcing the OP version to compile through casting doesn't help in making `B[A2](new A3(), "")` compile, as `String` doesn't conform to `A2#T`. – Eduardo Pareja Tobes Dec 21 '16 at 21:19
2

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.

pedrofurla
  • 12,763
  • 1
  • 38
  • 49
1

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)
}
Stefan
  • 207
  • 2
  • 8