2

I am learning Scala and was experimenting in the command line. I am not able to understand why the second line fails to compile. Can someone please explain.

val list = List(1,2,3,4)
list.map(_+1)           //This works. List(2,3,4,5)
list.map(List(_+1))     //But, Compilation Fails here
list.map(x=>List(x+1))  //This works. List(List(2),List(3),List(4),List(5))

Thank you.

2

2 Answers 2

8

Scala will expand _ (when used in a placeholder position) to x => x, except when such expansion would result in the identity function (more on that at the end of this answer):

list.map(_+1)       // same as list.map(x => x + 1)       (1)
list.map(List(_+1)) // same as list.map(List(x => x+1))   (2)

In the case of (1), scala can infer that x has type Int (since list: List[Int]).
However (2) fails with

error: missing parameter type for expanded function ((x$1) => x$1.$plus(1))

because scala can't infer x's type in List(x => x+1).


About the expansion and identity function:

scala> list.map(List(_))
res3: List[List[Int]] = List(List(1), List(2), List(3), List(4))

works, because the list.map(List(x => x)) expansion is rejected, and the next possible one is list.map(x => List(x)), which gives res3.

Sign up to request clarification or add additional context in comments.

4 Comments

Gah, beat me to it.
"Scala will expand _ to x => x, except when such expansion would result in the identity function". Isn't something wrong with this statement? x => x is the identity function.
@VictorMoroz: Ah yes, I might have to rephrase this. What I mean is: expanding f(_) to f(x => x) would replace _ with the identity function. Expanding f(_ + 1) with f(x => x + 1) doesn't.
My understanding is that _ alone will never be replaced with the identity function, check this: List(1).map(_). I'm not quite sure where it's declared in the language spec.
1

The type of list is List[Int]. So, the argument to list.map(...) has to be a function from Int to something.

What it seems you are trying to create with List(_+1) is a list of functions from integers to integers that happen to have one element: the successor function.

It fails to compile because this is one of the cases where the Scala compiler is unable to infer the type of an expression. But actually, a List[_] is a valid function from Int to something:

val f = List(99, 88, 77)
f(1)                     // -> 88

So for your code to compile, you have to give the compiler a little hint so it typechecks:

val list = List(1, 2, 3, 4)
list.map(List[Function[Int, Int]](_ + 1))
//           ^^^^^^^^^^^^^^^^^^^^ type annotation

This is also valid:

list.map(List[Int => Int]](_ + 1))

It will compile but it'll fail at runtime, because you are trying to get elements 1, 2, 3 and 4 of a one-element list. If the list contained only zeros (the only valid index for that particular list) it'd work:

val list = List(0, 0, 0)
list.map(List[Function[Int, Int]](_ + 1))
// -> evaluates to a list of three functions 

... or also if the list of functions contained enough elements for the index 1, 2, 3 and 4 to exist:

val list = List(1, 2, 3, 4)
list.map(List[Function[Int, Int]]( // a list of 5 functions:
  _ + 1,                           // 0) the successor function,
  _ * 10,                          // 1) the "append a zero" function,
  Math.abs,                        // 2) the absolute value function,
  _ / 2,                           // 3) the half function
  x => 2                           // 4) the constant function with value 2
))
// -> evaluates to a list of four functions. Can you guess which ones?

1 Comment

list.map(List((_:Int) + 1))) is arguably simpler than list.map(List[Function[Int, Int]](_ + 1))

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.