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