Multiline function literal as arguments in Scala
Asked Answered
C

2

19

I always wondered why sometimes with function literals we can ignore the curly brace even for multiple statements. To illustrate this, the syntax for a multiline function literal is to enclose the statements with curly braces. Like so,

val fl = (x: Int) => {
  println("Add 25 to "+x)
  x + 25
}

However, when you pass it to a single-argument function, you can ignore the required curly brace for the function literal.

So for a given function f,

def f( fl: Int => Int ) {
  println("Result is "+ fl(5))
}

You can call f() like this,

f( x=> {
  println("Add 25 to "+x)
  x + 25
})
-------------------------
Add 25 to 5
Result: 30

Or when you use curly braces instead of parenthesis in the function call, you can remove the inner curly braces from the function literal. So the following code will also work,

f{ x=>
  println("Add 25 to "+x)
  x + 25
}

The above code is more readable and I notice that a lot of examples use this syntax. However, is there any special rule that I may have missed, to explain why this is working as intended?

Counterforce answered 14/12, 2012 at 3:24 Comment(0)
G
30

There are just a couple of simple syntax rules. The appendix of the spec is worth perusing.

A function literal or anonymous function (6.23) will look like x => Expr or x => Block depending on whether the context is an Expr or a ResultExpr, respectively.

A function application (6.6) will look like f(Expr, Expr) or f BlockExpr, i.e., f{ Block }. That is, a BlockExpr is just a sequence of block statements inside {...}.

When you call f(g), then g is an Expr, so as a function literal, x => Expr. The Expr can be a BlockExpr, x => { ... }.

When you call f{ Block }, then f { x => ... } has the function literal in ResultExpr of a Block (which is just a sequence of statements, no braces required).

Here, it's obvious that the anon func is at the bottom of a block:

scala> def m(x: Int=>Int) = x(5)
m: (x: Int => Int)Int

scala> m {
     | val y = 7
     | x => // no brace
     | x+y+1
     | }
res0: Int = 13
Girasol answered 14/12, 2012 at 6:40 Comment(1)
This is the explanation I was looking for. Also, in addition to the sections of the specs you highlighted, one can also refer to the Blocks (6.11) section for better understanding.Counterforce
C
15

This is one of the things that make Scala beautiful to me.

The simple answer to your question is:

Parentheses ( ) are meant for single line constructions. For instance, this works:

  def f(fl: Int => Int) {
    println("Result is " + fl(5))
  }

  f(
   x =>
    x + 25)

  f(x => x + 25) // single line

and curly braces { } are meant for multiline statements. For instance, this works:

 f { 
   x =>
     println("Add 25 to " + x)
     x + 25
 }   

but this code doesn't work:

f ( 
  x =>
    println("Add 25 to " + x)
    x + 25
)

The compiler complains with the following message:

value x is not a member of Unit possible cause: maybe a semicolon is missing before `value x'?

If you add the semicolon, you'll get a syntax error caused by the unmatched parenthesis.

If you try to do this:

f { x => println("Add 25 to " + x) x + 25 }

The compiler will get back to you with the message:

value x is not a member of unit

Do you get that he is trying to find x as a member of unit. Like:

f { println("Add 25 to " + x).x.+(25) }

Which is clearly wrong.

If you add the inner curly braces, Like this:

f ( 
  x => {
    println("Add 25 to " + x)
    x + 25 
  }
)

This will also work, but you still have a multiline statement that is signaled by the usage of the curly braces. So the compiler knows what you want there is to first print and then add 25 to x.

I've been bitten before by those subtleties. Ever since, I've been paying attention to the way I code with those, because you'll code and read a lot of this when you're mainly using maps, flatMaps, foreachs, fors and currying.

Cheers!

Crandell answered 14/12, 2012 at 4:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.