That’s certainly one of the dark corners of Scala. There is a core where _
makes sense, and then there are some tacked on things that confuse the meaning. But arguably the situation has already improved a lot in Scala 3.
The best way to understand an underscore is as a hole, something that is left out. There are two fundamental modes in syntax. Definitions (including patterns) and uses (including expressions and types). An underscore in a definition is something we choose not to name. An underscore in a use is a hole that needs to be filled by an argument later, i.e. it is a function parameter. Those are the fundamentals, and I think they are quite defensible that way.
Underscores in imports behave a bit differently. A wildcard import means we leave a hole for what is imported, which means in this case everything is imported. So that’s different from _
in expressions. In retrospect, maybe we should have stuck with import p.*
for this. But I believe wildcard imports are not a major hurdle to understanding. Please speak up if you think I am wrong here.
On the other hand, the underscore in a renaming import
import p.{a => _}
is a defining occurrence, so we choose not to give a local name to the thing that gets imported. This is quite analogous to _
in patterns or definitions. So I would say that use of _
is again consistent with the intended meaning.
And then we have the awkward squad.
-
_
in a typeF[_]
means wildcard type in Scala 2, not type function. This was maybe OK before Scala acquired HK types, but is utterly confusing since. It’s fixed in Scala 3, where wildcard types are writtenF[?]
(underscore is still allowed at the moment for cross compilation with Scala 2 but the meaning will change to type function in the future.) -
f _
in expressions means “all following parameters” in Scala 2, instead of “one parameter”. This idiom is dropped in Scala 3. -
In a field definition
var x: T = _
, underscore means “no initializing assignment”. There’s an issue I have just opened about this. -
: _*
means “flatten to match a vararg parameter” in an argument list. It’s fairly arbitrary as syntax. I am not sure it is confused with_
since_*
is probably seen as a separate token.