Scala 中 def foo = {} 和 def foo() = {} 有什么区别?

发布于 2024-12-04 13:48:21 字数 891 浏览 1 评论 0原文

鉴于以下在 Scala 中定义函数的结构,您能否解释一下其中的区别以及含义是什么?

def foo = {}

def foo() = {}

更新

感谢您的快速回复。这些都很棒。对我来说唯一的问题是:

如果我省略括号,是否还有办法传递函数?这是我在 repl 中得到的内容:

scala> def foo = {}
foo: Unit

scala> def baz() = {}
baz: ()Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo)
<console>:10: error: type mismatch;
 found   : Unit
 required: () => Unit
              test(foo)
                   ^

scala> test(baz)
res1: () => Unit = <function0>

更新 2012-09-14

以下是我注意到的一些类似问题:

  1. 带括号和不带括号的函数之间的区别
  2. Scala 方法没有参数

Given the following constructs for defining a function in Scala, can you explain what the difference is, and what the implications will be?

def foo = {}

vs.

def foo() = {}

Update

Thanks for the quick responses. These are great. The only question that remains for me is:

If I omit the parenthesis, is there still a way to pass the function around? This is what I get in the repl:

scala> def foo = {}
foo: Unit

scala> def baz() = {}
baz: ()Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo)
<console>:10: error: type mismatch;
 found   : Unit
 required: () => Unit
              test(foo)
                   ^

scala> test(baz)
res1: () => Unit = <function0>

Update 2012-09-14

Here are some similar questions I noticed:

  1. Difference between function with parentheses and without
  2. Scala methods with no arguments

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

稀香 2024-12-11 13:48:21

如果您在定义中包含括号,则可以选择在调用该方法时省略它们。如果在定义中省略它们,则在调用方法时将无法使用它们。

scala> def foo() {}
foo: ()Unit

scala> def bar {}
bar: Unit

scala> foo

scala> bar()
<console>:12: error: Unit does not take parameters
       bar()
          ^

此外,您可以对高阶函数执行类似的操作:

scala> def baz(f: () => Unit) {}
baz: (f: () => Unit)Unit

scala> def bat(f: => Unit) {}
bat: (f: => Unit)Unit

scala> baz(foo)    

scala> baz(bar)
<console>:13: error: type mismatch;
 found   : Unit
 required: () => Unit
       baz(bar)
           ^
scala> bat(foo)

scala> bat(bar)  // both ok

这里 baz 将仅采用 foo() 而不是 bar。这个有什么用,我不知道。但它确实表明类型是不同的。

If you include the parentheses in the definition you can optionally omit them when you call the method. If you omit them in the definition you can't use them when you call the method.

scala> def foo() {}
foo: ()Unit

scala> def bar {}
bar: Unit

scala> foo

scala> bar()
<console>:12: error: Unit does not take parameters
       bar()
          ^

Additionally, you can do something similar with your higher order functions:

scala> def baz(f: () => Unit) {}
baz: (f: () => Unit)Unit

scala> def bat(f: => Unit) {}
bat: (f: => Unit)Unit

scala> baz(foo)    

scala> baz(bar)
<console>:13: error: type mismatch;
 found   : Unit
 required: () => Unit
       baz(bar)
           ^
scala> bat(foo)

scala> bat(bar)  // both ok

Here baz will only take foo() and not bar. What use this is, I don't know. But it does show that the types are distinct.

飘过的浮云 2024-12-11 13:48:21

让我复制我在重复的问题:

0 元数的 Scala 2.x 方法可以使用或不使用括号 () 进行定义。这用于向用户发出信号,表明该方法有某种副作用(例如打印到标准输出或破坏数据),而不是没有副作用,稍后可以将其实现为 val

请参阅Scala 编程

这种无参数方法在 Scala 中很常见。相比之下,使用空括号定义的方法(例如 def height(): Int)称为空括号方法。推荐的约定是,只要没有参数,就使用无参数方法,并且该方法仅通过读取包含对象的字段来访问可变状态(特别是,它不会更改可变状态)。

该约定支持统一访问原则[...]

总而言之,Scala 中鼓励将不带参数且无副作用的方法定义为无参数方法,即省略空括号。另一方面,您永远不应该定义一个没有括号的有副作用的方法,因为这样该方法的调用看起来就像一个字段选择。

术语

0 元数方法有一些令人困惑的术语,因此我将在此处创建一个表:

用 Scala 编程scala/scala 术语
def foo: Int无参数方法nullary method
def foo(): Int空括号方法nilary method

我说“nullary method”听起来很酷,但人们经常说错,读者也会感到困惑,所以我建议坚持使用无参数方法与空括号方法,除非你在拉取请求时人们已经在使用这些术语。

() 在 Scala 2.13 或 3.0 中不再是可选的

伟大的 () 插入, Martin Odersky 对 Scala 3 进行了更改,要求 () 调用使用 () 定义的方法。这记录在 Scala 3迁移指南为:

自动应用是调用空方法而不传递空参数列表的语法。

注意:迁移文档中的术语有误。它应该读作:

自动应用是调用empty-paren(或“nilary”)方法而不传递空参数列表的语法。

Scala 2.13 遵循 Scala 3.x,并弃用了 Eta-expand 中空括号方法的自动应用如果预期类型为 Function0,则为 0-arity 方法。此规则的一个值得注意的例外是 Java 定义的方法。我们可以继续调用Java方法,例如不使用()toString

Let me copy my answer I posted on a duplicated question:

A Scala 2.x method of 0-arity can be defined with or without parentheses (). This is used to signal the user that the method has some kind of side-effect (like printing out to std out or destroying data), as opposed to the one without, which can later be implemented as val.

See Programming in Scala:

Such parameterless methods are quite common in Scala. By contrast, methods defined with empty parentheses, such as def height(): Int, are called empty-paren methods. The recommended convention is to use a parameterless method whenever there are no parameters and the method accesses mutable state only by reading fields of the containing object (in particular, it does not change mutable state).

This convention supports the uniform access principle [...]

To summarize, it is encouraged style in Scala to define methods that take no parameters and have no side effects as parameterless methods, i.e., leaving off the empty parentheses. On the other hand, you should never define a method that has side-effects without parentheses, because then invocations of that method would look like a field selection.

Terminology

There are some confusing terminology around 0-arity methods, so I'll create a table here:

Programming in Scalascala/scala jargon
def foo: Intparameterless methodsnullary method
def foo(): Intempty-paren methodsnilary method

I sounds cool to say "nullary method", but often people say it wrong and the readers will also be confused, so I suggest sticking with parameterless vs empty-paren methods, unless you're on a pull request where people are already using the jargons.

() is no longer optional in Scala 2.13 or 3.0

In The great () insert, Martin Odersky made change to Scala 3 to require () to call a method defined with (). This is documented in Scala 3 Migration Guide as:

Auto-application is the syntax of calling a nullary method without passing an empty argument list.

Note: Migration document gets the term wrong. It should read as:

Auto-application is the syntax of calling a empty-paren (or "nilary") method without passing an empty argument list.

Scala 2.13, followed Scala 3.x and deprecated the auto application of empty-paren methods in Eta-expand 0-arity method if expected type is Function0. A notable exception to this rule is Java-defined methods. We can continue to call Java methods such as toString without ().

世界等同你 2024-12-11 13:48:21

要回答第二个问题,只需添加 _

scala> def foo = println("foo!")
foo: Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo _)
res10: () => Unit = <function0>

scala> test(foo _)()
foo!

scala>            

To answer your second question, just add an _:

scala> def foo = println("foo!")
foo: Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo _)
res10: () => Unit = <function0>

scala> test(foo _)()
foo!

scala>            
满身野味 2024-12-11 13:48:21

我建议始终以如下函数开始定义:

def bar {}

并且仅在某些情况下,当您被迫时,将其更改为:

def bar() {}

原因:让我们从可能的使用角度考虑这两个函数。如何获取它们的信息以及可以将它们传递到哪里。

我根本不会称其为函数:

def bar {}

它可以被调用为:

bar

但不能作为函数:

bar()

当我们定义带有按名称调用参数的高阶函数时,我们可以使用这个栏:

def bat(f: => Unit) {
    f //you must not use (), it will fail f()
}

我们应该记住,<代码>=> Unit - 甚至不是一个函数。您绝对不能像使用函数一样使用 thunk,因为您不能选择将其视为要存储或传递的函数值。您只能触发对实际参数表达式(任意数量)的求值。
Scala:将函数作为代码块传递大括号之间

() 定义的函数有更大的使用范围。它可以在相同的上下文中与 bar 完全相同地使用:

def foo() = {}
//invokation:
foo
//or as a function:
foo()

它可以传递到带有按名称调用参数的函数中:

bat(foo)

此外,如果我们定义一个高阶函数,它接受不是一个按名称调用的 pamameter,而是一个真正的函数:

def baz(f: () => Unit) {}

我们还可以将 foo 传递给 baz

baz(foo)

正如我们所看到的标准函数,如 foo code>有更大的使用范围。但是使用不带 () 定义的函数加上定义接受按名称调用参数的高阶函数,让我们可以使用更清晰的语法。

如果您不尝试归档更好、更具可读性的代码,或者如果您需要能够将代码片段传递给使用按名称调用参数定义的函数和使用真实函数定义的函数,那么定义您的代码函数作为标准函数:

def foo() {}

如果您喜欢编写更清晰易读的代码,并且您的函数没有副作用,请将函数定义为:

def bar {}

另外尝试定义您的高阶函数以接受按名称调用参数,但是不是一个函数。
只有在迫不得已的情况下,才使用前面的选项。

I would recommend always start definition with a function like:

def bar {}

and only in cases, when you are forced, to change it to:

def bar() {}

Reason: Let's consider these 2 functions from a point of possible usage. How they can be infoked AND where they can be passed.

I would not call this a function at all:

def bar {}

It can be invoked as:

bar

but not as a function:

bar()

We can use this bar when we define a higher-order function with a call-by-name parameter:

def bat(f: => Unit) {
    f //you must not use (), it will fail f()
}

We should remember, that => Unit - is not even a function. You absolutely cannot work with a thunk as if it's a function insofar as you cannot choose to treat it as Function value to be stored or passed around. You can only trigger evaluations of the actual argument expression (any number of them).
Scala: passing function as block of code between curly braces

A function, defined with () has a bigger scope for usage. It can be used exactly, in the same context, as bar:

def foo() = {}
//invokation:
foo
//or as a function:
foo()

It can be passed into a function with a call-by-name parameter:

bat(foo)

Additionally, if we define a higher-order function, that accepts not a call-by-name pamameter, but a real function:

def baz(f: () => Unit) {}

We can also pass foo to the baz:

baz(foo)

As we can see standard functions like foo have a bigger scope for usage. But using a functions defined without () plus defining higher-order functions, that accept call-by-name parameter, let us use more clear syntax.

If you do not try to archive a better, more readable code, or if you need ability to pass your piece of code both to function defined with a call-by-name parameter and to a function defined with a real function, then define your function as standard one:

def foo() {}

If you prefer to write more clear and readable code, AND your function has no side-effects, define a function as:

def bar {}

PLUS try to define your higher-order function to accept a call-by-name parameter, but not a function.
Only when you are forced, only in this case use the previous option.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文