15

Can someone explain me what exactly is going on here? I am not fully getting into it right now:

val s = Seq(1D,2D,3D,4D)
case class WithUnit(value: Double, unit: String)
s map { WithUnit(_,"cm") } // works
s map { WithUnit(value = _ , unit = "cm") } // error: missing parameter type for expanded function ((x$2) => value = x$2)

I guess the compiler can´t infer the parameter type because I wrote the name of the argument. But why not? It shouldn´t be more difficult only because of stating the name of the argument?!

Thanks!

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
Peter Schmitz
  • 5,824
  • 4
  • 26
  • 48

2 Answers2

31

When you wrote:

 WithUnit(value = _, unit = "cm")

You wanted it to mean:

 x => WithUnit(value = x, unit = "cm")

But if you take a close look at the error message, you'll see that the compiler didn't see it that way, it parsed it as:

 WithUnit(x => value = x, unit = "cm"})

As you can see, the _ is scoped more tightly than you wanted.

_ always picks the tightest non-degenerate scope it can. The scope is determined purely syntactically, during parsing, without regard to types.

By non-degenerate, I mean that the compiler didn't think you meant:

WithUnit(value = x => x, unit = "cm")

Tightest non-degenerate scope means the scope defined by the innermost function parenthesis relative to the underscore. Without such a rule the compiler wouldn't be able to know which _ corresponds to which function when function calls are nested.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
  • 1
    Ahh, I see! Now I have a deeper understanding.... _tightest non-degenerate scope_ ... I will remember that! Thank you! – Peter Schmitz Mar 10 '11 at 13:11
  • 1
    Arguably, though, this is a bug. `WithUnit(_, "cm")` worked because it was treated as a partially applied function, not as a an anonymous function literal. It doesn't get treated the same way when used with named parameters, which I suspect was just an oversight. – Daniel C. Sobral Mar 10 '11 at 20:46
  • 1
    @Daniel: I don't think the distinction you're drawing between "partially applied function" and "anonymous function literal" exists. – Seth Tisue Mar 10 '11 at 21:03
  • 1
    Arguably, this is a bug now that `f(id = e)` can no longer mean assignment. The syntax used to be "assignment or named arg", now it is only "named arg", and the expression is `_`, and the OP's intuitive reading is correct. – som-snytt Feb 03 '19 at 06:01
3

Try this:

scala> val withUnits = s map { x => WithUnit(value = x, unit = "cm") }
withUnits: Seq[WithUnit] = List(WithUnit(1.0,cm), WithUnit(2.0,cm), WithUnit(3.0,cm), WithUnit(4.0,cm))

The problem is the usage of the underscore to directly define an anynymous function.

A detailed description is in chapter 8.5. Placeholder syntax in the "Programming in Scala" book.

michael.kebe
  • 10,916
  • 3
  • 44
  • 62