Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - Lower bound inference in path-dependent types

I'm trying to understand why can't the Scala compiler infer the following restriction on a path-dependent type:

trait MyTrait
class MyTraitImpl extends MyTrait
trait MyTrait2[A <: MyTrait] {
  type MyTraitType = A
}
class MyTrait2Impl[A <: MyTrait] extends MyTrait2[A]

val obj: MyTrait2[_] = new MyTrait2Impl[MyTraitImpl]

def myMethod[A <: MyTrait](t2: MyTrait2[A]) = println("Hi!")

myMethod[obj.MyTraitType](obj)
// <console>:14: error: type arguments [obj.MyTraitType] do not conform to method myMethod's type parameter bounds [A <: MyTrait]
//               myMethod[obj.MyTraitType](obj)

For me, intuitively, MyTraitType can't be anything other than a subclass of a MyTrait, as the bound is right on A in MyTrait2. If there is, can you give me an example or point me to where this code snippet is wrong?

If this is a Scala compiler limitation, can anyone show me a way to achieve this using the type system? Note that:

  • I do not have a MyTrait object, nor does myMethod receive one;
  • I do not need myMethod to know the concrete type of A; all it needs to know is that A it is a subtype of MyTrait and that t2 is parametrized on A;
  • The underscore in obj is intentional; where I call myMethod, I don't know the concrete type of A (or else it would not be a problem);
  • I prefer solutions where I don't have to modify myMethod.
like image 262
Rui Gonçalves Avatar asked Oct 04 '22 07:10

Rui Gonçalves


1 Answers

You should just use constraints on type member instead of bounds on type parameter in MyTrait2 declaration:

trait MyTrait
class MyTraitImpl extends MyTrait
trait MyTrait2 { // Remove [A <: MyTrait]
  type MyTraitType <: MyTrait // add <: MyTrait
}
class MyTrait2Impl[A <: MyTrait] extends MyTrait2 { type MyTraitType = A }

val obj: MyTrait2 = new MyTrait2Impl[MyTraitImpl]

def myMethod[A <: MyTrait](t2: MyTrait2{ type MyTraitType = A }) = println("Hi!")

myMethod[obj.MyTraitType](obj)

You'll get a compilation error on wrong types, just as expected:

scala> val otherObj: MyTrait2 = new MyTrait2Impl[MyTraitImpl]
otherObj: MyTrait2 = MyTrait2Impl@8afcd0c

scala> myMethod[obj.MyTraitType](otherObj)
<console>:15: error: type mismatch;
 found   : otherObj.type (with underlying type MyTrait2)
 required: MyTrait2{type MyTraitType = obj.MyTraitType}
              myMethod[obj.MyTraitType](otherObj)
                                        ^

Proof it works with List[MyTrait2]:

scala> for {
     |   obj <- List[MyTrait2](
     |            new MyTrait2Impl[MyTraitImpl],
     |            new MyTrait2Impl[MyTraitImpl]
     |          )
     | } myMethod[obj.MyTraitType](obj)
Hi!
Hi!
like image 186
senia Avatar answered Oct 12 '22 02:10

senia