Scala 3: Contextual Abstractions, Part I
By: Dean Wampler
Tags: Programming Scala, Scala, Scala 3

This is my second post about new features in Scala 3.

Update: November 17, 2020. The extension method example for ~> now uses the new name for the @alpha annotation, @targetName , as of 3.0.0-M2. Also, it didn’t make sense to use the @infix annotation, so I removed it. Update: December 27, 2020. The syntax for given instances and extension methods was refined slightly in Scala 3.0.0-M3. Update: February, 2021. The syntax for extension methods was refined slightly in Scala 3.0.0-RC1.

Water Tree © 2007, Dean Wampler

One of Scala’s most famous features is implicits, a mechanism used for many idioms, like simulating extension methods (defined below), passing arguments implicitly to methods (rather than explicitly), constraining allowed types, and other idioms. Collectively, these idioms are contextual abstractions.

Mastering Scala has required learning idiomatic use of the implicit mechanism to implement these idioms. While powerful, this is a challenge for beginners.

Scala 3 begins a transition towards new constructs designed to tip the balance more towards clear expression of intent over a generic, but widely-applicable mechanism. This transition will happen over many releases, primarily to make it easier for teams to adopt the new constructs, rather than forcing potentially large-scale code changes. Even Scala itself needs this transition period, as the collections library is the same as in Scala 2.13, which uses implicits extensively.

Hence, all the old uses of implicits still work in Scala 3 (with a few small modifications for better safety), while encouraging you to begin the transition.

This is a big topic, one to which I devote two chapters in Programming Scala, Third Edition. I’ll use several posts here, but even then, I can only give you a taste of these changes. Of course you should read my book 😁. You can also find all the details in the Dotty documentation.

Implicit Conversions and Extension Methods

One way to create a two-element tuple in Scala is to use a -> b , an alternative to (a, b) . In Scala 2, this is implemented with an implicit conversion from a 's type to ArrowAssoc , which provides the -> method:

Note that the Unicode arrow → is deprecated. I won’t explain the other details here, like @inline . (Okay, it tells the compiler to inline the code, avoiding method invocation overhead…)

This is very common in Scala 2 code; you want a method to appear to be part of a type, so you perform an implicit conversion to some wrapper type that provides the method.

Put another way, Scala 2 uses a general-purpose mechanism, implicits, to achieve a specific intent, creation of extension methods, a mechanism in other languages (like C#) for adding methods to types outside the type definitions. Scala 3 adds first-class support for extension methods.

Here’s how you could rewrite ArrowAssoc , where I use ~> as the method name, since ArrowAssoc still exists in Scala 3!

Using an extension method instead. From the book’s code examples.

Two forms are shown. The first form puts all the needed type parameters, [A,B] in this case, after the extension keyword. The second form, which looks closer to what we already know for parameterized types and methods, was introduced in Scala 3.0.0-RC1.

The type being extended is A and the value a is used to refer to the instance (i.e., like this ). Note that I’m using the optional syntax I discussed in my previous post. You can put as many methods as you want after the initial extension ... line.

One other new thing in Scala 3 is the @targetName annotation. It provides an alphanumeric name for the “operator” method. This name can’t be used in Scala code (e.g., a.arrow2(b) ), but it can be used in Java code to call the method. This is the new recommendation for all operator methods; use the @targetName annotation.

Whither Implicit Conversions?

With extension methods, you’ll write far fewer conversions, but sometimes you need to convert between types because a context needs one type and the user supplies another. For example, you have a finance application with case classes for USD, percentages, and salary, but you want to allow the user the convenience of specifying double literal values with implicit conversions to the domain types. Consider this Scala 3 REPL session:

New Scala 3 conversions. Adapted from this example from the book.

I first enable the implicit conversion language feature with the import statement on line 1. Then I define three conventional case classes, which provide nicely formatted strings. (They could also do input validation, etc.)

Line 12 and 15 show the new way of declaring an implicit conversion. The given keyword replaces declaring an implicit def . It is nearly the same thing, but there can be subtle differences. Note that a method is created for each one. The name prefix given_Conversion is generated by the compiler for anonymous given instances. When an explicit name is provided, the name:Type syntax is used, as shown.

The abstract class Conversion is a new type. It has an abstract method apply that the compiler will implement for you with an anonymous function implementation as shown on the right-hand side of the = above. You can also provide the definition explicitly for longer implementations. Here is the Dollars conversion written this way:

Note the use of with , similar to how it is used when mixing in traits as in Scala 2. In general, when a given instance implements an abstraction, with is used as the keyword signaling the beginning of the body of definitions.

Back to the longer example, next we see another new feature on line 18. You can insert underscores _ in long numeric literals to make them easier to read. (You may have seen this in languages like Python.) This line uses Double literal arguments, but the conversions create Dollars and Percentage instances.

Scala 3 still supports the Scala 2 mechanism that uses an implicit method. Here’s another way to get Dollars :

The older mechanism may be removed eventually.

What’s Next?

That’s enough for now. In the next post, I’ll explore new syntax for type classes, which combines given with extension methods to implement an interface that defines the type class. For example, think about how you might add a toJSON method to a hierarchy of domain types (like Dollars , etc. above) or how you might implement the category theory concepts like monad and monoid.