【问题标题】:Why are Scala class methods not first-class citizens?为什么 Scala 类方法不是一等公民?
【发布时间】:2015-08-10 07:30:20
【问题描述】:

我刚刚开始使用 Scala 并正在修改工作表。例如:

def merp(str: String) : String = s"Merrrrrrrp $str"
val merp2 = (str: String) => s"Merrrrrrrp $str"
val merp3 = (str: String) => merp(str)
val merp4 = merp _
merp("rjkghleghe")
merp4("rjkghleghe")

以及对应的工作表结果:

merp: merp[](val str: String) => String
merp2: String => String = <function1>
merp3: String => String = <function1>
merp4: String => String = <function1>
res0: String = Merrrrrrrp rjkghleghe
res1: String = Merrrrrrrp rjkghleghe

说,例如,val merp5 = merp 会产生错误,因为显然方法不能像函数那样是值。但我仍然可以将方法作为参数传递。我在下面的代码 sn-p 中演示了这一点,改编自a similar SO question

def intCombiner(a: Int, b: Int) : String = s"herrrrrrp $a derrrrrrp $b"
def etaAbstractor[A, B](combineFoo: (A, B) ⇒ String, a: A, b: B) = combineFoo(a, b)
etaAbstractor(intCombiner, 15, 16)

工作表结果:

intCombiner: intCombiner[](val a: Int,val b: Int) => String
etaAbstractor: etaAbstractor[A,B](val combineFoo: (A, B) => String,val a: A,val b: B) => String
res10: String = herrrrrrp 15 derrrrrrp 16

  1. method-not-being-first-class 是一种限制,可能是由 Scala 的 JVM 交互强加的,还是语言设计中的决定?
  2. 为什么我需要像merp3 一样滚动我自己的eta abstractions
  3. merp4 也是 eta abstraction,还是有点相似?
  4. 为什么我的etaAbstractor 有效? Scala 是否正在悄悄地将 intCombiner 替换为 intCombiner _

欢迎提供理论、计算机科学的答案,以及指向language specification 中任何相关点的指针。谢谢!

【问题讨论】:

  • Scala 的设计和实现 在许多领域都与 Java/JVM 兼容性密切相关——我敢肯定,如果没有这些,差异会更小,但你d 必须问 Martin(我认为他有时会访问 SO)或找到一些较早的 impl 注释。尽管可以使用method _ 轻松“提升”它们。

标签: function scala methods


【解决方案1】:

免责声明:我不是计算机科学家,但我会尝试猜测:

  1. 方法是对象的一部分,不存在于对象之外。你不能单独传递方法。闭包是另一种(等效?)封装状态的方法,通过将对象方法转换为独立函数(顺便说一下,这只是另一个在 Scala 中具有apply() 方法的对象),您正在创建一个闭包。这个过程被称为 eta 扩展。 §3.3.1, §6.26.5

  2. 您不必这样做。你也可以写val merp3 : (String =&gt; String) = merp§6.26.5

  3. 是的,merp4 也是 eta 扩展。 §6.7

  4. §6.26.2

【讨论】:

  • Python 和 JavaScript 的方法一流的函数,所以 #1 是“在 Scala(和 Java)中......”。
  • @user2864740 在 Python 中无法调用未绑定的方法,因此在我看来它不是一流的函数。分配绑定方法看起来像引擎盖下的 eta-expansion。如果你在你的函数中使用this,Javascript 也会有同样的问题,只是你将undefined 作为this 的值而不是错误。
  • 方法仍然是不是一流的值,而是按名称(或message passing OO)调用的“定义的表现形式”。维护上下文(或不维护)不会影响他们的公民身份:或根本的实施差异。
  • 非常感谢您提供的规格链接。为了完成 Q#1,我推测这是一个语言设计决策,由 Scala 代码如何被翻译到 JVM 中产生。具体来说,一个方法(作为没有值的抽象)可以在编译时完成,而 Function——作为一个对象,其伴随的闭包是一个动态树结构——只能在运行时创建。为了使方法具体化,必须创建一个Function 对象。这可能会对性能产生影响,因此要求程序员明确创建该对象而不是误导是一个不错的决定。
【解决方案2】:

它与etaAbstractor 一起工作的原因是编译器可以推断出需要一个函数(不是函数调用)。

如果我不得不猜测为什么在无法推断函数类型的情况下需要下划线,我认为这是为了改进常见错误类别的错误报告(获取调用所在的函数故意的)。但同样,这只是一个猜测。

在 JVM 中,方法不是对象,而一流的函数必须是对象。所以方法必须装箱成对象才能转换成函数。

【讨论】:

    猜你喜欢
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-01
    • 2012-09-08
    相关资源
    最近更新 更多