首页 > 解决方案 > Scala 中没有参数和名称参数 eta 扩展混淆

问题描述

我有以下代码

def call1 = 4
call1 _

并收到以下警告:

没有参数列表和按名称参数的方法不能再转换为函数 as ,而是m _写一个函数文字() => m

我不确定是否完全理解这里的原因和部分句子。显然,我在这里没有名称参数。

首先是警告可以分成两部分:

1)

没有参数列表的方法不能再转换为函数 as ,而是m _写一个函数文字() => m

按名称参数不能再转换为函数 as ,而是m _写一个函数文字() => m

我正在寻求 1) 和 2) 的解释。在第 2 点上,我很难想象为什么以及何时将别名参数转换为函数。

标签: scala

解决方案


两者都可以通过以下方式证明:

def needFunction(f: () => String) = {
  println("calling function")
  println(f())
}

def paramLess: String = {
  println("calculating value")
  "foo"
}

我想paramLess传入needFunction. 什么时候评估?

needFunction(() => paramLess) // (1)
// calling function
// calculating value
// foo

这很明显——paramLess将在里面进行评估needFunction。但是这个?

needFunction(paramLess _) // (2)

(2)是一样的,(1)但我需要花点时间思考一下它会如何表现并在 REPL 中运行它来确定。

这会经常发生,尤其是在使用别名参数时——您想让一些 API 易于用户使用,因此您将thunk: => A其作为参数,但在内部您传递() => A的是确切知道何时进行评估(将一个别名传递给另一个别名等等,希望两者之间没有任何评估会导致一些非常脆弱的代码)。

带参数的函数和方法没有可读性问题——如果你不把()它放在不被评估的地方。您可以编写它、传递它、返回它 - 但如果没有传递参数,则没有评估。无参数方法在您每次使用它们的名称时都会进行评估,因此如果它们执行副作用或一些昂贵的计算,它们会很棘手 - 然而,这是避免和不鼓励的,因为建议您至少将()参数列表添加到副作用方法。同时,它们有点假装是不需要争论的价值观。显然,这里的社区决定,如果他们想伪装成值,就应该将它们用作值,() => value作为创建函数的方式——如果我们不将它们用于副作用,这是有道理的。

By-name 参数在可读性方面是一个类似的情况,但是 - 从我所见 - 它们有很多极端情况,当与 eta-expansion 结合使用时会导致错误,而且它们也经常(几乎总是?)出现在副作用的背景

def inSomeContext(context: Arguments)(thunk: => SideEffects) = ...
// Try.apply
// IO.apply
// option.fold(noneByName)(some => ...)
// ...

这使得它们比无参数方法(不鼓励副作用)更加危险,因此警告更加合理。

最重要的是,它主要是关于可读性、安全性和避免意外:

  • 在可以用作values 时使用不带参数的方法(初始化顺序可能是原因之一)而没有副作用和意外(在无参数方法中有更多警告警告副作用),
  • 和名称参数作为在您的 API 中使用的好东西,但它会在内部立即手动转换为您可以安全传递的函数(甚至是() => A某种函数)或值 ( val evaluated = thunk)。

对这些其他用法的警告有助于保持代码的可读性并且没有意外。


推荐阅读