Scala:Function0 vs by-name参数

时间:2017-09-12 21:17:43

标签: scala functional-programming lazy-evaluation

任何人都可以就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(因为没有懒惰类型,所以无论如何都没有意义)

1 个答案:

答案 0 :(得分:6)

好的,这里有完整的故障。

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(值)

  2. 这个失败是因为f1期待一个单位=> Int函数,但是给出了Int值。

    1.   

      F1(方法)

    2. 这个有效,因为f1期待一个函数,并且给出了一个方法。这里有区别:方法不是Scala中的值;它不能单独存在,而是它在(类,特征,对象等)中定义的上下文的属性。功能是一个价值;它可以保存在一个集合中,作为另一个函数中的参数,从函数等返回。当编译器期望一个函数并给出一个方法时,它执行 eta扩展。给定一个例如f: (a: Int, b: Int) => Int,eta扩展是在保留签名的同时创建另一层的过程,因此它变为(a: Int, b: Int) => f(a, b)。这种技术很有用,因为我们可以将方法转化为函数。给定一些方法def f(a: Int): Int = ???,我们可以执行eta-expansion来创建Int =>类型的函数。 Int out of it:(a: Int) => f(a)。如果您有兴趣,我前段时间就写了一篇blog post

      1.   

        F2(值)

      2. 毫无意外地工作,但请注意每次在函数体中使用时都会访问传递的值。

        1.   

          F2(方法)

        2. 工作,但警告我们正在调用一个用空括号定义的方法,不使用括号。好的做法是使用没有括号的方法(例如f),当它们只是表示一个值时,但是每次访问时都会重新计算一个值,例如:numberOfUpvotesf(),并且当执行某种副作用时使用带有空括号的方法(例如createSnapshot()),因此该方法不是幂等的,例如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 (同样,这不应出现在纯函数代码中)。

          建议:不要以什么为替代,以此来阻碍你的思想。不要使用替代品。如果某些东西需要一个功能,请提供一个功能。如果需要值,请提供值。如果定义了没有parens的方法,则在没有parens的情况下调用它。如果它有parens,请用parens调用它。

          如果你需要从一个方法转到另一个函数并且编译器期望一个函数,那么eta-expansion将自动发生。如果它不期望某个功能,您需要手动完成。

          {{1}}

          最后一种情况是部分应用的功能。我觉得我现在走得太宽了所以我会在这里停下来。我希望这会有所帮助。

相关问题