What is the difference between def foo = {} and def foo() = {} in Scala?
Asked Answered
C

4

56

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
Cardamom answered 13/9, 2011 at 22:54 Comment(1)
The def keyword defines a method, not a function, though the difference can often be subtle. Think along the same lines as int vs Integer in Java: the former can be autoboxed into the latter.Yardarm
M
41

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.

Maemaeander answered 13/9, 2011 at 23:15 Comment(3)
Interesting. If Scala programmers follow the rationale/design critera that Eugene Yokota cites, that would mean that "baz" accepts only functions that may have side effects, while "bat" also accepts functions without side effects. Not sure if there's any practical application for that; my gut feeling is that there might be more useful to be able to write a function that only accepts functions without side effects (because it does some sort of parallel processing and side effects might cause issues), but suggesting that the supplied function should have side effects might have its place.Boru
"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." -> This helps explain a question I had about this comment in docs.scala-lang.org/tutorials/scala-for-java-programmers.html : "A small problem of the methods re and im is that, in order to call them, one has to put an empty pair of parenthesis after their name". In fact, the example they give with the method "def re() = real" does work with and without. Thank you.Loeb
So basically if you omit the parenthesis in the definition you have to omit them when you call the method. Also you should never omit parenthesis when the method has side effects because it looks like a property call.Makeshift
A
41

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 Scala scala/scala jargon
def foo: Int parameterless methods nullary method
def foo(): Int empty-paren methods nilary 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 ().

Astilbe answered 30/9, 2011 at 5:16 Comment(2)
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. - But invocation of a method defined with parens can still be called without parens, so I don't buy this argument.Germanize
@AndrewMcKinlay Yeah, it's a bit of an odd convention, particularly when you note that accessing mutable fields is a side effect.Greyhound
G
7

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>            
Greyhound answered 13/11, 2015 at 20:13 Comment(0)
L
0

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.

Loathing answered 28/2, 2018 at 11:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.