3
def fun(f: Int => Unit) {
  f(10)
  f(20)
}
println("method 1 call:")
fun(i => {println("hi"); println(i)})
println("method 2 call:")
fun{println("hi"); println(_)}

The output is:

E:\test\scala>scala i.scala
method 1 call:
hi
10
hi
20
method 2 call:
hi
10
20

I think i => {println("hi"); println(i)} and println("hi"); println(_) are the same. Because we have one parameter and the parameter is used just once, we can use _ to simplify the code.

Then, why does method 2 just print "hi" once? (Does it mean: if I want to use _ to simple the calling, the contents on the right of => just can have one expression, if have more than one, e.g. println("hi"); println(i); Then, we can not use _ to replace?

2 Answers 2

4

println(_) expands to x => println(x), so {println("hi"); println(_)} expands to {println("hi"); x => println(x)}. So when fun{println("hi"); println(_)} executes, the following steps take place:

  1. The expression {{println("hi"); println(_)}} is evaluated. Which means:

    1. println("hi") is evaluated and then
    2. x => println(x) is evaluated, creating a function object that will print its argument.
    3. The thus-created function object is the result of the expression.
  2. The method func is called with the created function object as its argument. func will call the function with 10 and 20, causing it to print those numbers.
Sign up to request clarification or add additional context in comments.

4 Comments

I think you just gived the description of the result, not the reason. As the definition of def fun(f: Int => Unit), what's the f in the definition of fun{println("hi");println(_)}? if println("hi") is not included in f, why?
Yes, this is what I really want to know. In fact, I want to realize the effect of method 1 call, meanwhile I want to simple the calling "fun(i => {println("hi"); println(i)})". So I used method 2 call, but find the effect is quite different from method 1 call. So I want to get the real reason, why println("hi") is not included in f in method 2 call?
@atline I believe I gave the real reason. {println("hi"); println(_)} does not expand to {x => {println("hi"); println(x)}}, but rather to {println("hi"); x => println(x)}. And that's why you see the behaviour you see. The reason it expands that way is that the expansion rules are pretty simple. For this case the relevant rule is simply that f(args...,_,...moreArgs) expands to x => f(args..., _, ...moreArgs). If you want to know why the rules are as they are, you might ask the designed, but it probably boils down to that simple rules tend to be better, having to look at the ...
... context of the expression complicates the expansion code (and logic) by quite a bit and it'd be impossible to match the programmer's intent in all cases anyway. Plus: the more complicated the rules, the harder it is for the programmer to understand what a given piece of code will expand to, even if he does know the rules.
1

First, you have to know that in scala { block; of; code } is an expression that evaluates to whatever the last expression inside it evaluates to.

When you say:

fun(i => { println("hi"); println(i) })

you create an anonymous function, which body contains 2 expressions, both returning () and both are evaluated when function is called, everything as expected.

But when you say

fun({println("hi"); println(_)})

You pass in a block, not an anonymous function. As sepp2k explained this expands to

{ println("hi"); x => println(x) }

So, you pass a block to fun, this block is being evaluated before it is passed. So first println("hi") happens, it is printed just once as block is evaluated once, and then this x => println(x) is evaluated, which is a function Int => Unit, that prints its argument. This, and only this (as a last expression is passed to the fun. This is why each time you call fun it just prints the argument twice.

To see further on how block could work you can look at this example that does more in the block

fun {
  println("building the function")
  val uuidOfThisFunction = UUID.randomUUID
  x => println(s"$uuidOfThisFunction, $x")
}

So this block prepares a function giving it some extra context through the closure. This uuid will stay the same for both calls that happen if fun.

building the function
86e74b5e-83b5-41f3-a71c-eeafcd1db2a0, 10
86e74b5e-83b5-41f3-a71c-eeafcd1db2a0, 20

The example that would look more like what you did with first call would be

fun(
  x => {
    println("calling the function")
    val uuidOfThisCall = UUID.randomUUID
    println(s"$uuidOfThisCall, $x")
  }
)

The block evaluates every time f is called.

calling the function
d3c9ff0a-84b4-47a3-8153-c002fa16d6c2, 10
calling the function
a0b1aa5b-c0ea-4047-858b-9db3d43d4983, 20

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.