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

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

问题描述

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

  def d1(a:Int,b:Int){} $ b $ (A,B)⇒单位){} 



我可以这样做:

  r(d1)

但是,当重载 r 时,它将不再有效:

<$ (A,B)⇒单位){}
def r [A,B,C](委托人:(A,B) ,C)⇒Unit){}

r(d1)//不再编译



  r(d1 _)

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

是否有任何方法可以通过显式转换完成以下操作?

<$ p (代表:( A,B)⇒单位){}
def r [A,B,C](委托人:(A,B, C)⇒单元){}

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

r(d1)//只能编译r(d1 _)
r(d2)// only compi les with r(d2 _)

有些类似的 question ,但没有完全解释。 / p>

解决方案

隐式是正确的术语,规范中的部分是6.26.2,并且这必须是重复问题所以人们会认为;这是稳定的行为)。



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



我会走出一条腿,说超载时,适用性会受到破坏,因为没有预期的类型(6.26.3,臭名昭着)。当不超载时,6.26.2适用(eta扩展),因为参数的类型决定了预期的类型。当超载时,arg是专门键入的,没有预期的类型,因此6.26.2不适用;因此, d 的重载变体都不适用。


从6.26开始。 3重载解析



否则,让S 1,。 。 。 ,S m是由
获得的类型的向量,其中每个参数的类型都是未定义的。

这里是隐式转换(所谓的),当你命名一个没有参数的方法时,如 r(d1)


6.26.2方法转换

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



评估。类型=> T的无参数方法m总是通过评估m所绑定的表达式将
转换为T类型。隐式应用程序。如果该方法仅使用隐式参数,则会按照§7.2的规则传递
隐式参数。



Eta Expansion。否则,如果该方法不是构造函数,并且
expected类型pt是函数类型(Ts)⇒T,则对表达式e执行eta-expansion
(§6.26.5)。



清空应用程序。否则,如果e有方法type()T,则隐式应用于空参数列表
,得到e()



<以下示例演示了在存在重载的情况下应用程序对eta-expansion的偏好。当eta-expansion不适用时,空应用程序是6.26.2中试图隐含的最终结果。换句话说,当超载时(这很容易混淆,并且足够邪恶),将 f 作为 f()按统一访问原则,但将 f 作为 f <是不自然或奇怪的。除非您确信预计会有函数类型。

  scala>对象栏{
| def r(f:()=> Int)= 1
| def r(i:Int)= 2
| }
定义模块Bar

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

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

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

预先通过形状进行筛选。形状测试封装了从未使用eta-expansion的直觉,因为参数arg没有预期的类型。这个例子表明,即使它是表达式检查的唯一方式,eta-expansion也不会被使用。

 阶>对象栏{
| def bar(f:()=> Int)= 1
| def bar(is:Array [Int])= 2
| }
定义的对象Bar

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

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

scala> Bar.bar(m)
< console>:10:error:重载的方法值栏及其替代方案:
(is:Array [Int])Int<
(f:()=> Int)Int
不能应用于(Int)
Bar.bar(m)
^

任何阅读这篇文章的人都会对这两次转换相关的问题


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) {}

I can do this:

r(d1)

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 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.

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.

From 6.26.3 Overloading Resolution

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

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 Method Conversions

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

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

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

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.

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...

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

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天全站免登陆