Let's start with the ones returning the results you expected:
println(l.map { x => g(x) })
println(l.map { g(_) })
println(l.map { g })
One can infer that all three are the same thing. Actually, the third one depends on the fact that map
expects a function, but, truthfully, they really are basically the same.
Starting with the last, g
is a named function. You defined it with the name g
, taking an Int
and returning a List[Int]
.
Next, the first one: x => g(x)
. This is an anonymous function. It doesn't have a name, and it's definition is right there, where it is written: it takes a parameter x
(which was inferred to be Int
), and returns a List[Int]
(because that's what calling g(x)
) does.
Now, g
and x => g(x)
are not the same thing. They have the same type, Int => List[Int]
, but they are different functions, just like, if I defined def h(x: Int) = g(x)
, h
and g
would have the same type but would not be the same function.
That leaves g(_)
. That is a syntactic sugar for x => g(x)
. In this case, g(_)
and x => g(x)
are really the same thing. This is a special case of something like _ + _
, an expression where the underscores represent the parameters to a function. One would think that g(_)
would be equal to g(x => x)
, but this case, where the expansion would be x => x
, is an exception that "moves" the function to the next outer expression boundary.
So, let's see the cases that gave you doubts:
println(l.map { (_: Int) => g(_) }) // line 2
First, we know that g(_)
is the same thing as x => g(x)
, so this line would be equivalent to
println(l.map { (_: Int) => (x => g(x)) }) // line 2
The remaining underscore is completely unrelated to the one in g(_)
. An underscore in place of a parameter name means the parameter name is irrelevant. It's essentially the same thing as writing this:
println(l.map { (unusedVar: Int) => (x => g(x)) }) // line 2
As for the type, it is Int => (Int => List[Int])
. So, when you map the list, you get a list of Int => List[Int]
-- functions! -- which is what gets printed.
println(l.map { (_) => g(_) }) // line 3
Same thing as line 2, except that you omit the type of the parameter, which is going to be inferred anyway.
Finally,
println(l.map { _ => }) // line 4
The type of that is Int => Unit
. It simply takes a parameter, which is ignored, doesn't do anything, and returns a Unit
(which is something like the void
type in Java).
Unit
is a type for things whose value doesn't matter. A function has to return something, because that's what functions do. When that something doesn't matter, we use the Unit
type, which has only one value. The sole value of Unit
is written as ()
.
For example, this returns true
:
def test = {
val a = println("Println returns Unit")
val b: Unit = () // the only possible value
a == b
}
And that's why you see a list of ()
get printed for line 4: it's a List[Unit]
, and, therefore, all elements of it are ()
.