你不知道,但是这个:
identifier expected but integer literal found.
someNumbers.filter(_:Int => _ % 5 == 0)
^
是一个可怕的错误!好吧,不是人们想要的那种感觉,但是你设法写了一些语法正确的东西,并且与你的意图完全不同。首先,让我们看看重写有效的代码:
someNumbers.map(_: Int => _ <:< Any)
现在,您会看到它与您编写的内容(直到错误位置)只有两个不同:%替换为<:和5替换为Any(如编译器所希望的那样——标识符而不是数字)。
你可以编译并运行上面的代码,它会返回一些东西,所以让我们试着分解它。首先,考虑以下两个陈述:
someNumbers.map(_ + 1)
someNumber.map(_) // does not compile
它们各自的含义略有不同,可以这样重写:
someNumbers.map(x => x + 1)
x => someNumbers.map(x)
第一个编译是因为在x声明参数时,编译器知道预期的类型。第二个没有编译,因为在编译器看到x时,它不知道将如何使用它。当然,这只是因为编译器重写了代码,但事情就是这样。
这里重要的是,当您添加 时: Int,编译器开始尝试编译您以第二种方式编写的内容。
你打算写的东西不是有效的代码。例如:
someNumbers.map(x => x + 1) // valid code
someNumbers.map((x: Int) => x + 1) // valid code
someNumbers.map(x : Int => x + 1) // "invalid" code
更准确地说,第三个例子是“无效的”,因为编译器不知道类型在哪里结束!要了解原因,请查看以下语句:
val f: Int => Int = x => x
在这里我们=>出现了两次,但每次都有不同的含义!第一种情况,Int => Int,是 的语法糖Function1[Int, Int]。换句话说,=>inInt => Int是类型的一部分。
在第二种情况下,x => x(大致)代表new Function1[Int,Int] { def apply(x: Int) = x }. 此=>代码中的 表示存在匿名函数,并将其参数与其主体分开。
现在我们可以理解编译器是如何解释的了someNumbers.filter(_: Int => _ % 5 == 0)。像这样:
someNumbers.filter(_: Int => _ % 5 == 0) // gets rewritten as
(x: Int => _ % 5 == 0) => someNumbers.filter(x)
意思Int => _ % 5 == 0是假定为类型。我们已经看到了为什么=>不停止它,但是错误只发生在5!这里和那里之间发生了什么?
首先,我将回到我的可编译示例。它使用了一个不太容易理解的 Scala 构造,也不是很常见:中缀类型表示法。该示例可以重写如下:
someNumbers.map(_: Int => _ <:< Any)
someNumbers.map(_: Int => <:<[_, Any])
换句话说,就像2 * 2代表2.*(2),Int Map String代表Map[Int, String]。这就解释了为什么编译器没有停下来%——它认为它代表一种类型——但它仍然给我们留下了_.
然而,在这一点上, 的含义_,尤其是重写后的形式,不应该看起来很神秘:它是一种存在类型!更具体地说,它是通配符存在类型。整个事情可以这样重写:
(x: Function1[Int,<:<[t, Any] forSome { type t }]) => someNumbers.map(x)
或者,没有任何语法糖(但在实现上略有不同*):
new Function1[Function1[Int, <:<[t, Any] forSome { type t }], List[<:<[q, Any] forSome { type q }]] {
def apply(x) = someNumbers.map(x)
}
现在,您不同意这与您想写的内容相去甚远吗?:-)
(*) 其实我觉得t和q原著是一样的,但是我还没有想出什么办法不用语法糖把它写出来。