Scala:Function0 vs by-name参数

web*_*ter 5 functional-programming scala lazy-evaluation

任何人都可以通过Scala编译器对名称参数=> TFunction0参数如何() => T相互转换给出明确的答案吗?我知道它们不一样,但差异非常微妙,因为它们可以在许多情况下互换使用.

示例:如果我定义

def someFunction: Int = 2
def f(x: => Int): Unit = println(x)
Run Code Online (Sandbox Code Playgroud)

然后我可以成功打电话

f(2)
f(someFunction)
Run Code Online (Sandbox Code Playgroud)

如何() => Int替代=> Int

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

另外,请纠正我,如果我错了以下的理由:=> T是从来没有一个可以接受的替代() => T,因为第一个是值类型(T),另一种是功能型.也就是说,如果我有def f(x: () => Int),我永远无法通过的Int,或懒惰Int(甚至没有任何意义,因为没有懒惰的类型).

slo*_*ouc 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"
Run Code Online (Sandbox Code Playgroud)
  1. f1(值)

该操作失败,因为f1期望有Unit => Int函数,但是给出了Int值。

  1. f1(方法)

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

  1. f2(值)

可以正常工作,但是请注意,每次在函数体中使用传递的值时,都将访问传递的值。

  1. f2(方法)

可行,但警告我们正在调用不使用括号的空括号定义的方法。优良作法是f在仅表示值的情况下使用不带括号的方法(例如),但是每次访问该值时都会重新计算numberOfUpvotes(例如f()),并且在执行某种副作用时使用带有空括号的方法(例如)。因此,该方法不是幂等的(例如createSnapshot(),同样,它不应在纯功能代码中出现)。

忠告:不要为替代什么而烦恼。不要使用替代品。如果某些东西需要功能,请为其提供功能。如果需要一个值,请提供一个值。如果定义的方法没有parens,则在没有parens的情况下调用它。如果它具有parens,请使用parens调用它。

如果您需要从方法转到函数,并且编译器需要一个函数,则将自动进行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
Run Code Online (Sandbox Code Playgroud)

最后一种情况是部分应用的函数。我觉得我现在太宽泛了,所以我会在这里停止。希望对您有所帮助。