在Scala中重载方法的方法和函数之间的Eta扩展 [英] Eta-expansion between methods and functions with overloaded methods in Scala
问题描述
我想了解为什么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不适用;因此,
从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屋!