2015-06-05 31 views
2

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

回答

5

看那toBuffertoList的定义:

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

正如你所看到的,toBuffer是通用的,而toList不是。 造成这种差异的原因是 - 我相信 - Buffer是不变的,而List是协变的。

比方说,我们有以下类别:

class Foo 
class Bar extends Foo 

因为List是协变的,你可以对Iterable[Bar]实例调用toList和治疗结果作为List[Foo]。 如果List在哪里不变,这不会是这种情况。 Buffer是不变的,如果toBuffer被定义为def toBuffer: Buffer[A]你同样无法治疗结果toBuffer (上Iterable[Bar]一个实例)作为Buffer[Foo]一个实例(如Buffer[Bar]是没有一分型Buffer[Foo],不像列表)。 但通过声明toBufferdef toBuffer[A1 >: A](注意添加类型参数A1),我们回到有toBuffer返回实例的Buffer[Foo]可能性: 所有我们需要的是explcitly设置A1到富,或让编译器推断它(如果toBuffer在期望Buffer[Foo]的站点被调用)。

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

当你这样做:

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

你从来没有明确说A1String,但由于"ab-cd".split("-")有明确的类型Array[String],编译器知道AString。它也知道A1 >: A(在toBuffer),并没有任何进一步的限制,它将推断A1正是A,换句话说String。 所以最后整个表达式返回一个Buffer[String]

但是这里的事情:在scala中,类型推断发生在整个表达式中。 当你有像a.b.c,你可能会以为阶将推断确切类型 为a,然后从该推断出一个确切的类型a.b,终于为a.b.c。并非如此。 类型推断是推迟到整个表达式a.b.c(参见scala规范“6.26。4局部类型推理 ”,‘案例1:选择’)

所以,回到你的问题,在表达"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]需要知道AR的确切类型,就像任何泛型类型一样)。 所以你需要以某种方式明确地告诉他,因为它无法完全推断出来。

这意味着你需要做的要么:

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

或者:

"ab-cd".split("-").toBuffer.map((_:String).toBuffer) 
+0

我只是重新阅读这个答案,非常有帮助,谢谢。 –