41

A few times I saw a Scala code like that:

object Doer{
   def doStuff(op: => Unit) {
      op
   }
}

Invoked in this way:

Doer.doStuff{
      println("Done")
}

What is strange for me is how a function is passed to another function as just a block of code between curly braces. And there is even no parentheses that normally mark the beginning and end of argument list.

What is the name of this Scala syntax/feature? In what cases I can use it? Where is it documented?

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
Piotr Sobczyk
  • 6,443
  • 7
  • 47
  • 70
  • 1
    I think it's just a syntactic sugar. The round parens aren't here, because you can omit round parens if the function has only one argument. And the piece of code within curly braces is the way to provide an anonymous function. – Ashalynd Mar 26 '14 at 18:53
  • 1
    @Ashalynd No. You can call `f(args)` or `f {block}` but you can't omit the punctuation except as infix `x op y`. The block is an anon fun only as `{ case x => }`. – som-snytt Mar 26 '14 at 20:56

1 Answers1

54

This is called either a nullary function or a thunk, and is an example of call-by-name evaluation: http://www.scala-lang.org/old/node/138

You can use nullaries pretty much anywhere you have a parameter list. They are basically just syntactic sugar around zero-argument functions that make them look like ordinary values, and are invoked whenever they are referenced.

So

def doSomething(op: => Unit) {
  op
}
doSomething {
  println("Hello!")
}

is exactly the same as:

def doSomething(op: () => Unit) {
  op()
}
doSomething(() => println("Hello!"))

The one thing to keep in mind with nullaries is they are invoked every time they are referenced, so something like:

def foo(op: => Int) = op + op
foo {
  println("foo")
  5
}

will print "foo" twice.

Edit: To expand on Randall's comment, one of the big ways that a nullary function differs from a zero-arg function is that nullaries are not first-class values. For example, you can have a List[() => Int] but you cannot have a List[=> Int]. And if you had something like:

def foo(i: => Int) = List(i)

you are not adding the nullary function to the list, only its return value.

Dan Simon
  • 12,891
  • 3
  • 49
  • 55
  • 4
    It is best not to think of thunks as functions. 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). – Randall Schulz Mar 26 '14 at 19:53
  • To clarify the comment, you can do: `def f(op: =>Int) = op _` and also `def g(op: =>Int) = f(op)`, so `g(8)` does not eval. Other words: a call-by-name param is a parameterless method type, like `def x`. – som-snytt Mar 26 '14 at 20:50
  • @som-snytt: I don't really think of that as a good way to think about by-name parameters, either. I'm opposed to trying comprehend concept A as a variation on concept B when there is really just a vague, incidental similarity between them… – Randall Schulz Mar 27 '14 at 00:37
  • @RandallSchulz Actually, it's not a metaphor, it's the precise way the spec defines `=>A` in 3.3.1 as an expr that is eval'd every time it's referenced, same as a by-name param. (For me, that helped me not think of how it is implemented or encoded.) – som-snytt Mar 27 '14 at 05:14
  • 1
    And why is it so hard to find any official documentation on that?! – Piotr Sobczyk Mar 27 '14 at 18:50
  • I found some information about call-by-name here: http://www.scala-lang.org/old/node/138. I'll add the link to my answer as well. – Dan Simon Mar 27 '14 at 19:00
  • @DanSimon could you cite a reference on `nullary` vs `zero-arg`? [Wikipedia](https://en.wikipedia.org/wiki/Arity#Nullary) suggests they're the same thing. – dev Jun 18 '15 at 16:37
  • Seems current docs are available on: https://docs.scala-lang.org/tour/automatic-closures.html – gabrielf Feb 17 '23 at 14:30