2017-02-11 88 views
3

简单的例子来说明该问题:为什么scala未能对f-绑定多态性进行类型推断?

trait WTF[W <: WTF[W]] { 
    def get : Int 
} 

trait Zero extends WTF[Zero] { 
    override def get : Int = 0 
} 
case object Zero extends Zero 

final case class Box(inner : Int) extends WTF[Box] { 
    override def get : Int = inner 
} 

def printWTF[W <: WTF[W]](w : W) = println(w.get) 

printWTF(Box(-1)) 
printWTF(Zero) 

Box是好的,但Zero产生错误:

WTF.scala:22: error: inferred type arguments [Zero.type] do not conform to method printWTF's type parameter bounds [W <: WTF[W]] 
    printWTF(Zero) 
^
WTF.scala:22: error: type mismatch; 
found : Zero.type 
required: W 
    printWTF(Zero) 
     ^
two errors found 

如果我手动注释的类型,它编译:

printWTF[Zero](Zero) 
printWTF(Zero : Zero) 

第一线路按预期工作。我经常遇到无法从参数中推断出类型参数的情况。例如def test[A](x : Int) : UnitA类型在参数签名中无处显示,因此应该手动指定它。

但是后者对我来说很模糊。我只是添加了总是为真的类型转换,奇迹般地编译器学习如何推断方法类型参数。但Zero始终是Zero类型,为什么编译器无法从我的提示中推断出它?

回答

2

案件对象Zero的类型为Zero.type,并且是WTF[Zero]的子类型。因此,当您拨打printWTF(Zero)时,编译器推断出W = Zero.type,但Zero.type <: WTF[Zero.type]为false,因此编译失败。

这在另一方面更复杂的签名应该工作:

def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]) = println(w.get) 

而且作为示范,这确实是正确的推断类型:

scala> def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]): (V, W) = ??? 
printWTF: [W <: WTF[W], V <: W](w: V with WTF[W])(V, W) 

scala> :type printWTF(Box(1)) 
(Box, Box) 

scala> :type printWTF(Zero) 
(Zero.type, Zero) 
+0

>案例对象零的类型为Zero.type和是WTF的子类型[0] 但是一般规则是你可以用任何子类替换任何类入口。第二行示例完全一样。它不会更改类型签名。 – ayvango

+0

我不认为任何人都知道Scala中完整的类型推断规则。我所知道的是,它不会在您的示例中重试所有超类型的'Zero.type',并且您必须像我一样使用一些额外的参数来帮助它。如果你的意思是你可以用'Zero.type'替换'WTF [Zero]中的'Zero',那么你错了,因为'WTF'中的'W'是不变的。 –

+0

我并不是说'WTF [Zero]里面的'Zero'可以替代。但任何地方,凡是预期的'Zero'参数都可以用'Zero.type'替代,因为函数调用允许这样做,'Function [Zero,T]'是Function [Zero.type,T]的子类型' – ayvango