输入toList和toBuffer之间的推断不一致

时间:2015-06-05 06:15:32

标签: scala type-inference

根据以下示例,调用xs.toList.map(_.toBuffer)成功,但xs.toBuffer.map(_.toBuffer)失败。但是当使用中间结果执行后者中的步骤时,它会成功。是什么导致这种不一致?

scala> "ab-cd".split("-").toBuffer
res0: scala.collection.mutable.Buffer[String] = ArrayBuffer(ab, cd)

scala> res0.map(_.toBuffer)
res1: scala.collection.mutable.Buffer[scala.collection.mutable.Buffer[Char]] = ArrayBuffer(ArrayBuffer(a, b), ArrayBuffer(c, d))

scala> "ab-cd".split("-").toBuffer.map(_.toBuffer)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toBuffer)
              "ab-cd".split("-").toBuffer.map(_.toBuffer)
                                              ^

scala> "ab-cd".split("-").toList.map(_.toBuffer)
res3: List[scala.collection.mutable.Buffer[Char]] = List(ArrayBuffer(a, b), ArrayBuffer(c, d))

1 个答案:

答案 0 :(得分:5)

查看toBuffertoList的定义:

def toBuffer[A1 >: A]: Buffer[A1]
def toList: List[A]

如您所见,toBuffer是通用的,而toList则不是。 这种差异的原因是 - 我相信 - Buffer是不变的,而List是协变的。

假设我们有以下类:

class Foo
class Bar extends Foo

由于List是协变的,您可以在toList的实例上调用Iterable[Bar],并将结果视为List[Foo]。 如果List处于不变量,则情况并非如此。 Buffer不变,如果toBuffer定义为def toBuffer: Buffer[A],您同样无法处理结果 toBuffer的实例(在Iterable[Bar]的实例上)作为Buffer[Foo]的实例(因为Buffer[Bar]不是Buffer[Foo]的子类型,与列表不同) 。 但是,通过将toBuffer声明为def toBuffer[A1 >: A](注意添加的类型参数A1),我们可以让toBuffer返回Buffer[Foo]的实例: 我们所需要的只是将A1 explcitly设置为Foo,或者让编译器推断它(如果在期望toBuffer的站点调用Buffer[Foo]。)

我认为这解释了toListtoBuffer定义不同的原因。 现在问题是toBuffer是通用的,这会严重影响推理。

执行此操作时:

"ab-cd".split("-").toBuffer

您从未明确说过A1String,但由于"ab-cd".split("-")明确地指定了类型Array[String],编译器知道A是{{1} }}。 它还知道String(在A1 >: A中),并且没有任何进一步的约束,它会推断toBuffer正好是A1,换句话说A。 所以最后整个表达式返回String

但是这就是事情:在scala中,类型推断发生在一个表达式中。 当你有Buffer[String]这样的东西时,你可能会期望scala会推断出一个确切的类型 对于a.b.c,然后从该推断得到a的确切类型,最后是a.b。不是这样。 类型推断 deferred 到整个表达式a.b.c(请参阅scala规范“6.26.4本地类型推断 “,”案例1:选择“)

因此,回到您的问题,在表达式a.b.c中,子表达式"ab-cd".split("-").toBuffer.map(_.toBuffer) 键入"ab-cd".split("-").toBuffer,而是 它保持类似Buffer[String]的类型。换句话说,Buffer[A1] forSome A1 >: String不是固定的,我们只是将约束A1带到下一步推理。 下一步是A1 >: String,其中map(_.toBuffer)定义为map。此处map[C](f: (B) ⇒ C): Buffer[B]实际上与B相同,但此时A1 仍然不完全清楚,我们只知道A1。 这就是我们的问题。编译器需要知道匿名函数A1 >: String的确切类型(仅仅因为实例化(_.toBuffer)需要知道Function1[A,R]A的确切类型,就像任何函数一样通用类型)。 所以你需要以某种方式告诉他,因为它无法完全推断出来。

这意味着您需要执行以下任一操作:

R

或者:

"ab-cd".split("-").toBuffer[String].map(_.toBuffer)