9

I'm new in scala. Is there any the differences between the high order function follow brace or parentheses block?

For example:

  • List(1, 2, 3).map(i=> i + 1)

  • List(1, 2, 3).map {i => i + 1}

Both of them get same result: List(2, 3, 4)

But for this example List(1, 2).map { println("Hi"); _ + 1 } The result is below and why 'Hi' just print once?

Hi
List[Int] = List(2, 3)
Little Roys
  • 5,383
  • 3
  • 30
  • 28

2 Answers2

15

A block in Scala is just an expression. Like parentheses, they are useful for grouping code together. Unlike parentheses, blocks don't contain just one expression, but can contain 1 or more expressions. The value of a block is the value of the last expression in it.

{ println("Hi"); _ + 1 } is equivalent to { println("Hi"); (i: Int) => i + 1 }. This is a block that prints out "Hi", and it's value is a function that adds one. The string is printed before execution exits the block, and the function that it produces doesn't know anything about the println.

Here are some examples of these rules:

list.map(i => i + 1)
//      ^----------^
//    function literal passed as argument

list.map(_ + 1)
//       ^---^
// Underscore shorthand for above

list.map({ i => i + 1 })
// Identical to above.
// The block only contains one expression, so it has the value of that expression
// Otherwise stated: { expr } === expr

list.map({ println("Hi"); _ + 1 })
//         ^-----2-----^  ^-3-^
//       ^------------1---------^
// 1: The argument to map is the value of this block
// 2: The first statement of the block prints something. This is only executed once,
//   because it's not the *block* being passed as argument, it's its value.
// 3: Function literal in underscore notation. This is the value of the block
//   and this is what map sees.
// Order of operations (approx. bytecode):
// load list onto stack
// load string "Hi" onto stack
// call println and pop string off stack
// create function (i => i + 1) on top of stack
// invoke map with argument (i => i + 1), popping list and function off stack

list.map { println("Hi"); _ + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}

list.map({ i => println("Hi"); i + 1 })
// Function literals grow as big as they can be.
// The block contains only one expression, which is (i => println("Hi"); i + 1)
// This function prints "Hi" and then returns i + 1
// This call to map will print "Hi" for every element

list.map { i => println("Hi"); i + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}

Additionally, there are by-name parameters to deal with. By-name parameters are declared like so:

def func(a: => String) // Not () => String, it's => String

When you have by-name parameter, then

func { println("x"); _ + 1 }

actually passes the whole block as argument. The block still evaluates to i => i + 1, but func is in control of when that evaluation happens. Specifically, the code of the block is turned into a Function0 and passed into func, which can call it as many times as it likes, with side effects. This can be used to great effect, essentially allowing normal functions to act like custom control flow operators:

@tailrec def repeat(i: Int)(op: => Any): Unit
= if(i == 0) ()
  else {
    require(i >= 0, s"negative repeat amount: $i")
    op // Evaluate op
    repeat(i - 1)(op) // Won't evaluate op; will let sub-call deal with it
  }

repeat(5) { println("Hi"); println("Bye") }
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye

Note how the parentheses are omitted around the block, which really makes this seem like the ability to define a control flow operator.

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • That's great! It's really helpful. – Little Roys Aug 23 '17 at 01:00
  • Nice ascii art! Please say "result expression" instead of "return value" of a block. A block isn't a function, it's a bunch of statements followed by the result at the end. My answer to the linked question above also said "hi." "Hi." On syntax of `list.map { x => }` see https://stackoverflow.com/a/13873899/1296806 – som-snytt Aug 23 '17 at 05:12
2

In general, you would use parentheses for enclosing simple function parameters:

l.map( x => x * 2 )

and curly braces for enclosing more complex code block or partial functions including case pattern matching:

l.map{ x =>
  val y = x * 2
  y
}

l.map{
  case x if x%2 == 0 => x * 2
  case _ => 0
}

As to the reason why Hi is only printed once, List(1, 2).map{ println("Hi"); _ + 1 } is no different from List(1, 2).map{ println("Hi"); x => x + 1 }. To include println in the map iteration:

List(1, 2).map{ x => println("Hi"); x + 1 }
// Hi
// Hi
// res1: List[Int] = List(2, 3)
Leo C
  • 22,006
  • 3
  • 26
  • 39