(With apologies to Bob Dylan.)
This post covers how some uses in Scala 2 of the underscore,
_, are being replaced with alternatives. The contexts include import and the new export statements and type parameter wild cards. Along the way, I’ll discuss other changes for import statements, the new export statement, and a syntax change for repeated parameter lists (a.k.a., variable argument lists).
Scala 3.0.0-RC1 is out! This means the language changes should be done. The discussion of import syntax changes below was the last major change. For a concise summary of most of the notable changes in Scala 3, see my Scala 3 Highlights.
In Scala 2, the underscore is used for placeholders in parameterized types, function arguments, and match expressions. These usages remain in Scala 3. Here are examples for type and function parameters and match expressions:
Here is an example of a for comprehension that does validation of fields in a case class (say for example from user input in a web form):
While the underscore is still used for placeholders, Scala 3 uses new characters for wild cards in type parameters and import statements. Here are some examples of type parameter wild cards:
Note the three uses of
? for type wild cards. I discussed given imports in Scala 3: Contextual Abstractions, Part III.
New Import Syntax
The RC1 release introduced changes to the import syntax, one of which replaces
* as the import wild card:
Note that one use for
_ is retained: hiding items. For aliasing an import, the old “arrow”,
=>, is replaced with a new keyword
as (which may be used for additional purposes in a future Scala release). This means that
=> now has one use, in function literals. As a convenience, it’s no longer necessary to use curly braces when aliasing a single imported item.
* as the wild card is consistent with many other languages, including Java. The original reason for using
_ instead of
* in Scala was to allow users to define members named
* (e.g., for multiplication) and to import them with
import foo.*. The
_ was already reserved for other purposes, so why not?
In practice, very few people actually need to write
import foo.*. In addition, since the original decision, Scala added the back tick “escape” mechanism. So, in Scala 3, you can write
import foo.`*`. Incidentally, if you’ve never used back ticks for purposes like these, here are other examples of where they are handy:
Like for other breaking changes, the old Scala 2 uses of
_ are still allowed in Scala 3.0. A subsequent release of Scala 3 will drop support for them.
New Export Feature
Scala 3 introduces an
export statement that makes it easier to add members to a type that are implemented by some internal object. Consider an example
Service that has an embedded authentication instance, but we want to make that instance’s methods part of the
Note that last line. We export the members of
auth that we want, using the same syntax as import statements.
In Scala 2, you would have to write methods in
Service that forward to the corresponding
auth methods. Export statements remove this boilerplate.
Repeated Parameters Syntax
Finally, let’s see another place where
_ is disappearing; the syntax for repeated parameters:
This change makes the syntax more consistent. Notice how
count is defined, which is unchanged from Scala 2. The
T* indicates that zero or more values can be provided. Scala holds them in a collection, which is why I can call
Notice what happens in line 3. Does Scala automatically convert the
Seq[T] into a
T*? No, in both Scala 2 and 3. This is how it should work, because the compiler can’t know if you intend to convert the sequence to repeated parameters or pass the sequence as a single parameter. Hence, the sequence is treated as a single
T value in line 3, so
1 is returned. Line 4 does the correct thing; using the same trailing
* to tell the compiler to do the conversion. The trailing
* is the same syntax as for the declared repeated parameter list, for consistency.
Similarly in the
tail* means zero or more elements of the sequence that are captured in a new collection
_*, we use the placeholder
_, to ignore them. In Scala 2, we would use
tail: _* and
_: _*, respectively (the space can be omitted).
Final Thoughts and What’s Next?
The many uses of
_ in Scala 2 was a disadvantage for beginners, because it was hard to read code and always know what the
_meant, until it became natural based on the context. However, the advantage of
_ was not having to remember which character to use for which situation. However, it was decided that the disadvantages outweigh the advantages, so … changes.
In the next post, I’ll discuss type lambdas, and throw in dependent function types, and polymorphic function types for good measure.
You can start reading the rough draft of Programming Scala, Third Edition on the O’Reilly Learning Platform. Currently, the first six chapters are available, including the two (five and six) that cover contextual abstractions.