The underscores are really only syntactic sugar. Person(_, _)
is directly translated to (a, b) => Person(a, b)
, just with generated names for the parameters, so both are totally the same. It actually happens before any typechecks whatsoever (you can check with scalac -Xshow-phases
, which will list phase 1 as parse source into ASTs, perform simple desugaring
, while typing is phase 4).
So the type inference has to happen in both cases, as both do not give types for the function parameters. The inference always goes from left to right, per parameter list. This is important, having both parameters to map2
in the same parameter list would reduce the compiler’s ability to infer types and we may have to specify them for the function.
With the multiple parameter lists, it works like @jducoeur says, the compiler looks at the first parameter list, with expects an Either[EE, B]
and the Parameter we give it is an Either[String, Age]
. So B
is definitely fixed.
For EE
, the compiler will look if String
is a supertype of E
. E
(from mkName
) is also String
, so no problem. If it wasn’t, the compiler would then look for the narrowest type, that is both a supertype of String
and E
.
Now we know EE
and B
, A
was already set to Name
from the Either we call map2
on, so only C
is left. This means, all types for the input to our function are known. The return type C
can be inferred from the body of the function, the last expression is of type Person
, so that’s what C
is.
I don’t feel like this is pushing inference hard, most higher-order functions use this. I rarely see lambdas with specified parameter types.
To better remember how the underscore syntax works, you can compare it with a fill-in-the-blanks text: you give an expression with blanks _
and the compiler fills them with parameters from left to right, one parameter per blank.