Reputation: 13590
Hi I was writing any possible variations of passing a function to map, my initial understanding that they would all produce the same result, but I found that the lines 2, 3, actually produced different output, and line 4 is a mystery to me
def g(v: Int) = List(v - 1, v, v + 1)
val l = List(1, 2, 3, 4, 5)
// map with some variations
println(l.map { x => g(x) })
println(l.map { (_: Int) => g(_) }) // line 2
println(l.map { (_) => g(_) }) // line 3
println(l.map { _ => }) // line 4
println(l.map { g(_) })
println(l.map { g })
Output:
List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
List(<function1>, <function1>, <function1>, <function1>, <function1>)
List(<function1>, <function1>, <function1>, <function1>, <function1>)
List((), (), (), (), ())
List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
Upvotes: 3
Views: 609
Reputation: 297305
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 ()
.
Upvotes: 3
Reputation: 55569
These are all equivalent ways of passing the application of the function g
to every element of the List
:
l.map { x => g(x) }
l.map { g(_) }
l.map { g }
res17: List[List[Int]] = List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
These are equivalent ways of mapping all of the elements of the List
to an unapplied function like g
:
l.map { (_: Int) => g(_) }
l.map { (_) => g(_) }
That is, each element of the mapped list is actually g
.
scala> l.map { (_: Int) => g(_) }.head
res23: Int => List[Int] = <function1>
scala> res23(0)
res24: List[Int] = List(-1, 0, 1)
In fact, the only difference between those two is the parenthesis and type annotation. They are both equivalent to:
l.map { _ => g(_) }
The following just maps all the elements of the List
to Unit
:
l.map { _ => }
Upvotes: 6