在 Scala 中使用重载方法在方法和函数之间进行 Eta 扩展 [英] Eta-expansion between methods and functions with overloaded methods in Scala

查看:19
本文介绍了在 Scala 中使用重载方法在方法和函数之间进行 Eta 扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解为什么 eta 扩展(第 6.26.5 节)对重载方法不起作用.例如,如果我有以下两种方法:

I would like to understand why the eta-expansion (§6.26.5) does not work for overloaded methods. For example, if I have following two methods:

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

我可以做到:

r(d1)

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

But, when overloading r it will no longer work:

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

r(d1) // no longer compiles

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

and I have to explicitly convert method into partially applied function:

r(d1 _)

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

Is there any way to accomplish following with the explicit conversion?

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 _)

有点类似问题,但没有完全解释.

There is somewhat similar question, but it is not fully explained.

推荐答案

Implicit 是正确的术语,规范中的部分是 6.26.2,这肯定是一个重复的问题(或者有人会认为;这是行为稳定).

Implicit is the correct term, and the section is 6.26.2 in the spec, and this must be a duplicate question (or so one would think; this is stable behavior).

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

The linked question also answers that the expected type must be a function.

我会冒昧地说,当过载时,由于没有预期的类型(6.26.3,臭名昭著),适用性受到破坏.未重载时,6.26.2 适用(eta 扩展),因为参数的类型决定了预期的类型.重载时,arg 是专门输入的,没有预期的类型,因此 6.26.2 不适用;因此,d 的两个重载变体都不适用.

I'll go out on a limb and say that when overloaded, applicability is undermined because there is no expected type (6.26.3, infamously). When not overloaded, 6.26.2 applies (eta expansion) because the type of the parameter determines the expected type. When overloaded, the arg is specifically typed with no expected type, hence 6.26.2 doesn't apply; therefore neither overloaded variant of d is deemed to be applicable.

来自 6.26.3 重载解析

From 6.26.3 Overloading Resolution

否则,令 S 1 , ..., S m 是通过以下方式获得的类型的向量使用未定义的预期类型键入每个参数.

Otherwise, let S 1 , . . . , S m be the vector of types obtained by typing each argument with an undefined expected type.

当你命名一个没有参数的方法时,这里是可用的隐式转换"(所谓的),如 r(d1).关于 eta 扩展的段落适用于此.

Here are the "implicit conversions" (so-called) available when you name a method without args, as in r(d1). The paragraph on eta expansion applies here.

6.26.2 方法转换

6.26.2 Method Conversions

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

The following four implicit conversions can be applied to methods which are not applied to some argument list.

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

Evaluation. A parameterless method m of type => T is always converted to type T by evaluating the expression to which m is bound.

隐式应用程序.如果该方法只接受隐式参数,隐式参数按照 §7.2 的规则传递.

Implicit Application. If the method takes only implicit parameters, implicit argu- ments are passed following the rules of §7.2.

Eta 扩展.否则,如果该方法不是构造函数,并且预期类型 pt 是一个函数类型 (Ts ) ⇒ T , eta-expansion(§6.26.5) 对表达式 e 执行.

Eta Expansion. Otherwise, if the method is not a constructor, and the expected type pt is a function type (Ts ) ⇒ T , eta-expansion (§6.26.5) is performed on the expression e.

空应用程序.否则,如果 e 具有方法类型 ()T ,则为隐式应用于空参数列表,产生 e()

Empty Application. Otherwise, if e has method type ()T , it is implicitly applied to the empty argument list, yielding e()

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

More post-green-check explanation...

以下示例演示了在存在重载的情况下,应用程序优先于 eta 扩展.当 eta-expansion 不适用时,空应用"是在 6.26.2 中尝试的最后一个隐式.换句话说,当重载时(从表面上看已经够混乱和邪恶了),根据统一访问原则,很自然地将 f 作为 f() ,但是将 f 当作 f _ 是不自然或奇怪的,除非您非常确定需要一个函数类型.

The following example demonstrates preferring application to eta-expansion in the presence of overloading. When eta-expansion doesn't apply, "empty application" is the final implicit to try in 6.26.2. In other words, when overloading (which is confusing and evil enough on the face of it), it is natural to take f as f() by the uniform access principle, but it is unnatural or weird to take f as f _ unless you're quite sure a function type is expected.

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.

Candidates for overloading resolution are pre-screened by "shape". The shape test encapsulates the intuition that eta-expansion is never used because args are typed without an expected type. This example shows that eta-expansion is not used even when it is "the only way for the expression to type check."

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)
                  ^

读到这里的任何人都会对与这两个转换相关的问题感到好奇.

Anyone reading this far will be curious about a related issue with these two conversions.

这篇关于在 Scala 中使用重载方法在方法和函数之间进行 Eta 扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆