你不知道,但是这个:
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
原著是一样的,但是我还没有想出什么办法不用语法糖把它写出来。