在Scala中使用重载方法在方法和函数之间进行扩展

时间:2013-06-26 15:25:39

标签: function scala methods implicit-conversion

我想了解为什么eta-expansion(§6.26.5)不适用于重载方法。例如,如果我有以下两种方法:

def d1(a: Int, b: Int) {}
def r[A, B](delegate: (A, B) ⇒ Unit) {}

我可以这样做:

r(d1)

但是,当重载r时,它将不再起作用:

def r[A, B](delegate: (A, B) ⇒ Unit) {}
def r[A, B, C](delegate: (A, B, C) ⇒ Unit) {}

r(d1) // no longer compiles

我必须将方法显式转换为部分应用的函数:

r(d1 _)

有没有办法通过显式转换完成以下操作?

def r[A, B](delegate: (A, B) ⇒ Unit) {}
def r[A, B, C](delegate: (A, B, C) ⇒ Unit) {}

def d1(a: Int, b: Int) {}
def d2(a: Int, b: Int, c: Int) {}

r(d1) // only compiles with r(d1 _)
r(d2) // only compiles with r(d2 _)

有些类似question,但尚未完全解释。

1 个答案:

答案 0 :(得分:15)

隐含是正确的术语,规范中的部分是6.26.2,这必须是一个重复的问题(或者人们会想到;这是稳定的行为)。

链接的问题也回答了预期的类型必须是函数。

我会指出,当超载时,适用性会受到损害,因为没有预期的类型(6.26.3,臭名昭着)。未过载时,6.26.2适用(eta扩展),因为参数的类型决定了预期的类型。当重载时,arg是专门输入的,没有预期的类型,因此6.26.2不适用;因此,d的超载变体均不被视为适用。

  

从6.26.3重载决议

     

否则,让S 1,.... 。 。 ,S m是由...获得的类型的向量   用未定义的期望类型键入每个参数。

以下是名为不带args的方法时可用的“隐式转换”(所谓的),如r(d1)中所示。关于eta扩展的段落适用于此。

  

6.26.2方法转换

     

以下四种隐式转换可应用于方法   这些不适用于某些参数列表。

     

评价。类型=>的无参数方法m T总是被转换   通过评估m绑定的表达式来键入T.

     

隐式申请。如果该方法仅采用隐式参数,   隐式论证按照§7.2的规则通过。

     

Eta扩张。否则,如果方法不是构造函数,那么   期望类型pt是函数类型(Ts)⇒T,eta-expansion   (§6.26.5)在表达式e。

上执行      

空申请。否则,如果e有方法类型()T,则为   隐式应用于空参数列表,产生e()

更多的绿色检查后解释...

以下示例演示了在存在重载时更喜欢应用程序进行eta扩展。当eta-expansion不适用时,“空应用程序”是6.26.2中最后隐含的。换句话说,当超载时(在其表面上令人困惑和邪恶),通过统一访问原则将f作为f()是很自然的,但是采取它是不自然或奇怪的ff _,除非您非常确定需要的是函数类型。

scala> object Bar {
     | def r(f: () => Int) = 1
     | def r(i: Int) = 2
     | }
defined module Bar

scala> def f() = 4
f: ()Int

scala> Bar.r(f)
res4: Int = 2

scala> Bar.r(f _)
res5: Int = 1

超载分辨率的候选人已经过“形状”预先筛选。形状测试封装了从未使用过eta-expansion的直觉,因为args是在没有预期类型的​​情况下键入的。这个例子表明即使是“表达式检查的唯一方法”,也不使用eta-expansion。

scala> object Bar {
     | def bar(f: () => Int) = 1
     | def bar(is: Array[Int]) = 2
     | }
defined object Bar

scala> def m() = 7
m: ()Int

scala> m _
res0: () => Int = <function0>

scala> Bar.bar(m)
<console>:10: error: overloaded method value bar with alternatives:
  (is: Array[Int])Int <and>
  (f: () => Int)Int
 cannot be applied to (Int)
              Bar.bar(m)
                  ^

任何阅读此内容的人都会对a related issue with these two conversions感到好奇。