处理_
在 Scala 中有点棘手,顺便说一句,我认为错误处理应该稍微改进一下。回到主题,看一下这个例子:
def twice(i: Int) = i * 2
def gt2(j: Int) = j > 2
List(1,2,3) filter gt2
这可以正常编译并按预期工作。然而,尝试组合函数会导致神秘错误:
List(1,2,3) filter gt2(twice(_))
error: missing parameter type for expanded function ((x$1) => twice(x$1))
List(1,2,3) filter gt2(twice(_))
^
发生了什么?基本上当 Scala 编译器看到下划线时binds it进入最直接的上下文,即twice(_)
在这种情况下。这意味着我们现在正在调用gt2()
with a function twice
作为一个论点。编译器知道这个函数接受一个参数并返回相同的类型。可以说它应该弄清楚这个参数的类型和返回类型是Int
基于twice()
签名,但是它使用x$1
暂时占位,直到他稍后弄清楚为止。
不幸的是,它无法做到这一点,因为gt2()
期望一个Int
当我们提供一个函数时(至少编译器是这么认为的)。
那么为什么:
List(1,2,3) filter {k => gt2(twice(k))}
工作?编译器不知道类型k
提前。然而它知道gt2
回报Boolean
并期望Int
。它也知道twice()
期望一个Int
并返回一个。这样它就可以推断出类型k
。另一方面,编译器从一开始就知道filter
期望Int => Boolean
.
话虽这么说,回到你的案例。单独下划线 ((_)
) 不考虑“context" 因此编译器会搜索另一个最直接的封闭上下文。!_
本身会被视为一个函数,以及_ == true
。但不仅仅是下划线。
那么在这种情况下,最接近的直接上下文(我确信有一个科学名称......)是什么?好吧,整个表达:
(x$1) => List(true, false).filter(x$1).size
编译器认为您正在尝试创建一个函数,该函数接受某些未知类型的参数并返回表达式类型的某些内容:List(true, false).filter(x$1).size
。再次可以说它应该能够弄清楚filter
takes Boolean => Boolean
并返回Int
(.size
),但显然事实并非如此。
所以,你可以做什么?您必须向编译器提示,应在较小的上下文中解释下划线。你可以说:
List(true,false) filter (_ == true)
List(true,false) filter (i => i)
List(true,false) filter identity