Scala:Function0与按名称参数 [英] Scala: Function0 vs by-name parameters

查看:366
本文介绍了Scala:Function0与按名称参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于Scala编译器如何将副名称参数=> TFunction0参数() => T相互转换,谁能给出明确的答案?我知道它们并不相同,但是区别非常细微,因为它们可以在许多情况下互换使用.

Can anyone give a definitive answer on how by-name parameters => T and Function0 parameters () => T are transformed into one another by the Scala compiler? I know they are not the same, but the difference is very subtle as they can be interchangeably used in many scenarios.

示例:如果我定义

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

那我可以成功打电话

f(2)
f(someFunction)

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

更一般地说,() => T是通用的替代名称=> T参数的替代方法吗?

More generally, is () => T a universally acceptable replacement for a by-name => T parameter?

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

Also, please correct me if I'm wrong on the following reasoning: => T is never an acceptable replacement for () => T because the first is a value type (T), the other is a function type. That is, if I have def f(x: () => Int), I'll never be able to pass an Int, or a lazy Int (doesn't even make sense anyway as there are no lazy types).

推荐答案

好的,这是完整的细分.

Alright, here's the full breakdown.

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"

f1(值)

f1(value)

此操作失败,因为f1期望使用Unit => Int函数,但是给出了Int值.

This one fails because f1 is expecting a Unit => Int function, but is given an Int value.

f1(方法)

f1(method)

之所以起作用,是因为f1需要一个函数,并为其提供了一种方法.区别在于:方法在Scala中不是值;它不能单独存在,并且是它在(类,特征,对象等)中定义的上下文的属性.函数是一个值;它可以保存在一个集合中,作为另一个函数的参数,从一个函数返回等.当编译器期望一个函数并被赋予一个方法时,它将执行 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).我写了一个博客文章如果您有兴趣的话,大约在一段时间之前.

This one works because f1 is expecting a function, and is given a method. Here's the difference: method is not a value in Scala; it cannot exist on its own and is an attribute of the context it's defined in (class, trait, object etc.). Function is a value; it can be kept in a collection, taken as argument in another function, returned from a function etc. When the compiler is expecting a function and is given a method, it performs eta expansion. Given a function e.g. f: (a: Int, b: Int) => Int, eta expansion is a process of creation of another layer around that while preserving the signature, so it becomes (a: Int, b: Int) => f(a, b). This technique is useful because we can turn methods into function. Given some method def f(a: Int): Int = ???, we can perform eta-expansion to create a function of type Int => Int out of it: (a: Int) => f(a). I wrote a blog post about this some time ago if you're interested.

f2(值)

f2(value)

工作时不会感到惊讶,但要注意一个事实,每次在函数体中使用传递的值时,都将访问传递的值.

Works without surprises, but pay attention to the fact that the passed value is accessed every time it's used in the function body.

f2(方法)

f2(method)

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

Works, but with a warning that we are invoking a method that is defined with empty parenthesis by using no parenthesis. Good practice is to use methods without parenthesis (e.g. f) when they simply represent a value, but one that is recalculated every time it's accessed, e.g. numberOfUpvotes, and to use methods with empty parenthesis (e.g. f()) when some kind of side-effect is performed and hence the method isn't idempotent, e.g. createSnapshot() (again, this should not be present in purely functional code).

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

Word of advice: don't encumber your mind with what's a replacement for what. Don't use replacements. If something needs a function, provide it a function. If it needs a value, provide a value. If a method is defined without parens, invoke it without parens. If it has parens, invoke it with parens.

如果您需要从方法转到函数,并且编译器需要一个函数,则将自动进行eta扩展.如果不希望使用该功能,则需要手动进行操作.

If you need to go from method to function and compiler is expecting a function, eta-expansion will happen automatically. If it's not expecting a function, you need to do it manually.

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

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

Last case is a partially applied function. I feel like I'm going too broad now so I will stop here. I hope this helped.

这篇关于Scala:Function0与按名称参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆