Scala - 路径依赖类型的下界推断

时间:2013-08-07 11:48:18

标签: scala type-inference path-dependent-type

我试图理解为什么Scala编译器不能推断出对路径依赖类型的以下限制:

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)

对我而言,直观地说,MyTraitType不能是MyTrait的子类,因为绑定在A MyTrait2上。如果有,你能给我一个例子或指出我这个代码片段错误的地方吗?

如果这是Scala编译器限制,有人能告诉我使用类型系统实现此目的的方法吗?请注意:

  • 我没有MyTrait个对象,myMethod也没有收到;
  • 我不需要myMethod知道A的具体类型;它需要知道的是A它是MyTrait的子类型,t2上的A被参数化;
  • obj中的下划线是有意的;在我调用myMethod的地方,我不知道A的具体类型(否则它不会成为问题);
  • 我更喜欢我不需要修改myMethod的解决方案。

2 个答案:

答案 0 :(得分:3)

你应该只使用类型成员的约束而不是MyTrait2声明中的类型参数的边界:

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)

您会在错误的类型上遇到编译错误,正如预期的那样:

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)
                                        ^

证明它适用于List[MyTrait2]

scala> for {
     |   obj <- List[MyTrait2](
     |            new MyTrait2Impl[MyTraitImpl],
     |            new MyTrait2Impl[MyTraitImpl]
     |          )
     | } myMethod[obj.MyTraitType](obj)
Hi!
Hi!

答案 1 :(得分:1)

如果你真的想保留泛型参数(我同意,抽象类型在这种情况下更好),你可以做到以下几点:

限制通配符类型obj

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

在没有类型参数的情况下调用myMethod并让编译器弄明白:

myMethod(obj)

这只不过是通过类型推断帮助编译器。关于MyTrait2#MyTraitType的理由当然是正确的。

另一种解决方案(进入同一类别)是删除obj

的类型
val obj = new MyTrait2Impl[MyTraitImpl]

但是,当然,这可能不适用于您的实际用例。