Scala:Function0 与名称参数

Scala: Function0 vs by-name parameters

谁能就 Scala 编译器如何将别名参数 => TFunction0 参数 () => T 相互转换给出明确的答案?我知道它们不一样,但区别非常细微,因为它们可以在许多场景中互换使用。

例子:如果我定义

def someFunction: Int = 2
def f(x: => Int): Unit = println(x)

那我就可以成功调用了

f(2)
f(someFunction)

() => Int 如何成为 => Int 的可接受替代品?

更一般地说,() => T 是普遍接受的别名 => T 参数的替代品吗?

此外,如果我在以下推理上有误,请纠正我:=> T 永远不是 () => T 的可接受替代品,因为第一个是值类型 (T) ,另一个是函数类型。也就是说,如果我有 def f(x: () => Int),我将永远无法传递 Int 或惰性 Int(因为没有惰性类型,所以无论如何都没有意义) .

好的,这是完整的细目分类。

def value: Int = ???
def method(): Int = ???

def f1(f: () => Int) = ???
def f2(f: => Int) = ???

f1(value)  // fails
f1(method) // works
f2(value)  // works
f2(method) // works with a warning "empty-paren method accessed as parameterless"
  1. f1(value)

这个失败了,因为 f1 需要一个 Unit => Int 函数,但被赋予了一个 Int 值。

  1. f1(method)

这个之所以有效,是因为 f1 需要一个函数,并且给出了一个方法。区别在于:方法在 Scala 中不是值;它不能单独存在,并且是它在(class、特征、对象等)中定义的上下文的属性。功能是一种价值;它可以保存在一个集合中,作为另一个函数的参数,从一个函数返回等。当编译器需要一个函数并被赋予一个方法时,它会执行 eta 扩展。给定一个函数,例如f: (a: Int, b: Int) => Int,eta扩展是在保留签名的同时围绕它创建另一层的过程,因此它变成了(a: Int, b: Int) => f(a, b)。这种技术很有用,因为我们可以将方法转化为函数。给定一些方法 def f(a: Int): Int = ???,我们可以执行 eta 扩展以从中创建一个 Int => Int 类型的函数:(a: Int) => f(a)。如果你感兴趣的话,我前段时间写了一篇关于这个的 blog post

  1. f2(value)

工作正常,但要注意每次在函数体中使用传递的值时都会访问它。

  1. f2(method)

有效,但警告我们正在通过不使用括号调用一个用空括号定义的方法。好的做法是使用不带括号的方法(例如 f),当它们只表示一个值时,但每次访问时都会重新计算,例如numberOfUpvotes,并在执行某种副作用时使用带有空括号的方法(例如 f()),因此该方法不是幂等的,例如createSnapshot()(同样,这不应该出现在纯功能代码中)。

忠告:不要用什么来代替什么来阻碍你的思想。不要使用替代品。如果某物需要一个功能,就给它提供一个功能。如果它需要一个值,请提供一个值。如果一个方法是在没有括号的情况下定义的,则在没有括号的情况下调用它。如果它有括号,则用括号调用它。

如果您需要从方法转到函数并且编译器需要一个函数,eta 扩展将自动发生。如果它不需要功能,则需要手动执行。

def f(): Int = ???
val a = f               // no function context; a is a string
val b: () => Int = f    // b is a function Unit => Int
val c = f2 _            // c is a function Unit => Int

最后一个例子是偏应用函数。我觉得我现在说得太宽泛了,所以我会在这里停下来。希望对您有所帮助。