【问题标题】:Eta-expansion between methods and functions with overloaded methods in ScalaScala中具有重载方法的方法和函数之间的Eta扩展
【发布时间】:2013-06-23 20:07:48
【问题描述】:

我想了解为什么 eta 扩展(第 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有点类似,但没有完全解释。

【问题讨论】:

  • 有趣!顺便说一句,我不认为“隐式转换”是在这里使用的正确术语(至少对于 Scala),因为没有涉及 implicit def
  • 我认为它叫做隐式η-conversion
  • 我觉得@gzm0 是对的,我把它改成了“自动”
  • 它被称为 eta-expansion (§6.26.5) - 相应地更新了问题。
  • 请注意,您可以使用r[Int, Int](d1),尽管这比r(d1 _) 更冗长

标签: function scala methods implicit-conversion


【解决方案1】:

隐式是正确的术语,该部分是规范中的 6.26.2,这一定是一个重复的问题(或者有人会这么认为;这是一种稳定的行为)。

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

我会说,当超载时,由于没有预期的类型(6.26.3,臭名昭著),因此会破坏适用性。未重载时,应用 6.26.2(eta 扩展),因为参数的类型决定了预期的类型。重载时,arg 是专门键入的,没有预期的类型,因此 6.26.2 不适用;因此,d 的重载变体均不适用。

从 6.26.3 重载分辨率

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

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

6.26.2 方法转换

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

评估。类型 => T 的无参数方法 m 始终被转换 通过计算 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() 是很自然的,但是将f 当作是不自然或奇怪的f _ 除非你很确定需要一个函数类型。

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感到好奇。

【讨论】:

  • 感谢您指出规范中的相关部分(此处适用的是 6.23.3 和 6.26.5)。我不认为这是重复的问题(或者至少我没有找到任何问题)。很明显,预期类型必须是函数,但我想知道为什么表达式的预期类型(delegate)是函数类型(§6.25.2)时不能应用 eta 转换 - 显然不是从 eta 的角度来看。
  • 我没有尝试过设置花哨的编译器选项,但问题不是 eta 扩展的观点,而是重载解析的观点。您要求输入 r,因此您的 d1 在重载解析期间没有预期类型,这意味着没有 eta 扩展(没有自动 d1 _)。
  • 我认为是这样,我误解了重载解决方案。如何从您的答案中删除 6.26.2 中的引文,因为它无关紧要。
  • 这就是定义“必须是预期的函数类型”的规则的地方。我将编辑我的括号以澄清。
  • 好吧,我只是觉得它太冗长(引用太长),也许从重载决议中引用会更有益?
猜你喜欢
  • 2019-03-02
  • 1970-01-01
  • 2012-06-15
  • 2014-06-11
  • 2016-04-03
  • 2013-04-08
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多