Why does ((_: Int, _: Int) => _ / _) not compile when ((_: Int) / (_: Int)) does?

139 Views Asked by At

I am learning Scala and have a very basic question. Consider the following two expressions using the placeholder syntax -

// Syntax A  
val fnA = (_: Int, _: Int) => _ / _

// Syntax B
val fnB = (_: Int) / (_: Int) 

and their attempted applications -

// Syntax A
fnA(33, 3)

// Syntax B
fnB(33, 3)

Out of these two, only B and App(B) are valid syntax and I am not sure why. If the compiler is able to infer the arguments (and the order to apply them in) in fnB why is it not able to do it for fnA? I am basing my question on the premise that fnB is a shorthand for fnA and I am pretty certain there's a flaw in that reasoning. I am just not sure what the flaw is.

1

There are 1 best solutions below

0
On BEST ANSWER

The meaning of underscores on the left of arrow =>

(_: Int, _: Int) => ...

are different from the meaning of underscores around the infix operator /

(_: Int) / (_: Int) 

The former mean something like

"We are defining a function that takes two arguments, however we will not use these arguments in the body of the function"

whilst the latter is a shorthand for defining a function of two arguments

(x: Int, y: Int) => x / y

For example,

(_: Int, _: Int) => (_: Int) / (_: Int)

desugars to something like

(a: Int, b: Int) => ((x: Int, y: Int) => x / y)

where we see arguments a and b are not used in the body which happens to be yet another function ((x: Int, y: Int) => x / y).

Daniel documents the two meanings as

_ + _             // Anonymous function placeholder parameter
_ => 5            // Discarded parameter

As a side-note, consider a somewhat analogous situation at the typelevel involving type constructors and their bounds where the meaning of underscore depends on the context

CC[_] <: Iterable[_]  

Personally, my mind tricks me to think it is equivalent to

CC[x] <: Iterable[x]  

however that is not the case, and the meaning of the underscore on the left of <: is different from the meaning on the right

CC[x] <: Iterable[y] forSome {type y}

Note how x does not appear in Iterable[y] forSome {type y}. Adrian documents this under Common Pitfalls:

...perhaps surprisingly, CC[_] <: Traversable[_] is not equivalent to CC[X] <: Traversable[X]. The former expands to CC[X] <: Traversable[T] forSome {type T}, where T is existentially bound and thus unrelated to X.

and in a comment to type bounds on type constructor

comparing the level of types and values, it is quite unfortunate that the type-level underscore behaves differently from the value-level underscore. At the value level, _ + _ indeed stands for (x, y) => x + y, a function. As hinted at in my other comment, the type-level underscore is context-sensitive, and it never introduces a type-level function. It's either an anonymous type parameter definition or an anonymous existential. Neither of these have value-level equivalents.

Hence we should be careful to interpret the meaning of the underscore within its context.