简体   繁体   English

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

[英]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: 我试图理解为什么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)

For me, intuitively, MyTraitType can't be anything other than a subclass of a MyTrait , as the bound is right on A in MyTrait2 . 对我来说,直观, MyTraitType不能超过一个的子类以外的任何MyTrait ,因为绑定是正确的AMyTrait2 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? 如果这是Scala编译器限制,任何人都可以告诉我使用类型系统实现此目的的方法吗? Note that: 注意:

  • I do not have a MyTrait object, nor does myMethod receive one; 我没有MyTrait对象, myMethod也没有收到;
  • I do not need myMethod to know the concrete type of A ; 我不需要myMethod来了解A的具体类型; all it needs to know is that A it is a subtype of MyTrait and that t2 is parametrized on A ; 它需要知道的是A它是MyTrait的子类型,并且t2A上被参数化;
  • The underscore in obj is intentional; obj的下划线是故意的; where I call myMethod , I don't know the concrete type of A (or else it would not be a problem); 我称之为myMethod ,我不知道A的具体类型(否则它不会成为问题);
  • I prefer solutions where I don't have to modify myMethod . 我更喜欢解决方案,我不需要修改myMethod

You should just use constraints on type member instead of bounds on type parameter in MyTrait2 declaration: 您应该只在类型成员上使用约束而不是在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)

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] : 证明它适用于List[MyTrait2]

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

If you really want to keep the generic parameter (I do agree, abstract types would be nicer in this case), you can do the following: 如果你真的想保留泛型参数(我同意,在这种情况下抽象类型会更好),你可以执行以下操作:

Restrict the wildcard type of obj : 限制obj的通配符类型:

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

Call myMethod without type parameters and let the compiler figure it out: 在没有类型参数的情况下调用myMethod并让编译器弄明白:

myMethod(obj)

This is nothing more than helping the compiler out a bit with the type inference. 这只不过是帮助编译器进行类型推断。 Your reasoning about MyTrait2#MyTraitType of course is correct. 你对MyTrait2#MyTraitType的推理当然是正确的。

Another solution (going into the same category) is dropping the type of obj : 另一个解决方案(进入同一类别)是删除obj的类型:

val obj = new MyTrait2Impl[MyTraitImpl]

But of course, that might not apply in your real use case. 但是,当然,这可能不适用于您的实际用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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