首页 > 解决方案 > 为什么我不能只对 Scala 中的第一个参数应用下划线?

问题描述

我不知道为什么模式 d 在下面的列表中不好。为什么需要显式类型声明?

def adder1(m:Int,n:Int) = m + n

val a = adder1(2,_) //OK
val b = adder1(_,2) //OK

def adder2(m:Int)(n:Int) = m + n

val c = adder2(2)(_)     //OK
val d = adder2(_)(2)     //NG:missing parameter type
val e = adder2(_:Int)(2) //OK

我只想知道为什么模式 d 需要参数类型。非常欢迎仅显示引用语言规范。

标签: scala

解决方案


所以我相信这来自Partial Application的概念。

直观地说,部分函数应用程序说“如果你修复函数的第一个参数,你会得到一个剩余参数的函数”

...

Scala 使用占位符实现可选的部分应用程序,例如def add(x: Int, y: Int) = {x+y}; add(1, _: Int)返回一个递增函数。Scala 还支持多个参数列表作为 currying,例如def add(x: Int)(y: Int) = {x+y}; add(1) _.

让我们来看看adder2

来自 REPL:

scala> def adder2(m:Int)(n:Int) = m + n
def adder2(m: Int)(n: Int): Int

让我们得到一个值来表示:

scala> val adder2Value = adder2
                ^
       error: missing argument list for method adder2
       Unapplied methods are only converted to functions when a function type is expected.
       You can make this conversion explicit by writing `adder2 _` or `adder2(_)(_)` instead of `adder2`.

好的,让我们试试:

val adder2Value = adder2 _
val adder2Value: Int => (Int => Int) = $Lambda$1382/0x0000000840703840@4b66a923

啊哈!

英文:“一个接受一个Int并返回一个函数的函数,一个接受一个Int并返回一个Int

我们如何使用这个签名绑定第二个参数?除非我们首先通过外部函数,否则我们如何访问内部函数?

据我所知,除非您明确定义第一个参数的类型,否则使用此签名是不可能的。

(但是呢adder2(_)(_)?)

scala> adder2(_)(_)
              ^
       error: missing parameter type for expanded function ((<x$1: error>, x$2) => adder2(x$1)(x$2))
                 ^
       error: missing parameter type for expanded function ((<x$1: error>, <x$2: error>) => adder2(x$1)(x$2))

(也许这暗示了我们的解决方案?)

注意如果我们明确定义两个参数会发生什么:

val adder2Value2= adder2Value (_:Int) (_:Int)
val adder2Value2: (Int, Int) => Int = $Lambda$1394/0x000000084070d840@32f7d983

这更易于管理,我们现在可以修复任一参数,并获得简化的偏函数:

scala> val adder2FirstArg = adder2Value (_:Int) (10)
val adder2FirstArg: Int => Int = $Lambda$1395/0x000000084070d040@47f5ddf4

scala> val adder2SecondArg = adder2Value (5) (_:Int)
val adder2SecondArg: Int => Int = $Lambda$1396/0x000000084070c840@21ed7ce

那么这里到底发生了什么?

当你将一个参数绑定到一个值时,你已经明确地表达了类型(也许它是推断出来的,但它绝对是那个类型,在这种情况下是Ints)。它是糖,所以我们不需要写它。但在底层,这些是组合函数,它们的组合方式非常重要。为了能够匹配和简化函数签名,编译器要求我们以由外向内的方式提供此信息。否则,我们需要给它一些帮助才能到达那里。

编辑:

我认为这个问题更像是 Scala 语言规范。然而,益智练习。从设计的角度来看,我想不出任何好的理由,为此您需要以这样一种方式实现一个柯里化函数,即您不能对参数进行排序,以便最后一个参数是被推断的参数。


推荐阅读