1. Overview

In this tutorial, we’ll explore the improvements made to match expressions within Scala 3. Match expressions have always been a powerful feature within Scala, and the improvements made in Scala 3 make it an even more versatile and helpful feature to use within our application code.

We’ll go through each new feature individually, explaining what each one can do, and go through a working example.

2. No Need for Braces

One change made to Scala 3 is that braces are no longer required in many cases, and indentation can be used instead. This applies to match expressions, meaning something like:

strOpt match {
    case None => "Option is None"
    case Some(str) => s"Option contains $str"
}

Now becomes:

strOpt match
    case None => "Option is None"
    case Some(str) => s"Option contains $str"

This makes our code look much cleaner and, when used appropriately, can make our code easier to read.

3. Nested match

The next improvement we’ll discuss is nested matches. This allows us to match* on the result of another *match:

strOpt match {
    case None => "empty"
    case Some(str) => str
} match {
    case "empty" => "Option is None"
    case string => s"Option contains $string"
}

First of all, we match on an Option[String] and return a String. We then match on the resulting String and return another String explaining its contents.

This simplified example is trivial, as the same result can be achieved within a single match expression. However, it clearly shows the use of the nested match construct.

4. Calling match After Period

We can now call match directly on the expression we want to match on, using .match:

strOpt.match
    case None => true
    case Some(_) => false

This change does not alter how the match executes or any other syntax. It’s simply an advantage of match being usable as an alphabetical operator.

5. Using match in if Statement

We can also use a match expression in conjunction with the new control structures available in Scala 3. This can be done using a match resulting in a Boolean as the condition of an if statement:

if strOpt.match
    case None => true
    case Some(_) => false
then "nothing"
else "A string"

This a fantastic way to leverage the new features of control structures in our code to make if statements on the results of match expressions more concise.

6. Type Ascription in match

The final change made to the match expression is a difference in how type ascriptions work within matches. In Scala 2, this would be valid:

strOpt: Option[String] match
    case None => true
    case Some(_) => false

Whereas now, in Scala 3, type ascriptions within a match expression need to be contained within parentheses:

(strOpt: Option[String]) match
    case None => true
    case Some(_) => false

This is something to be aware of if upgrading a codebase from Scala 2 to Scala 3.

7. Conclusion

In this article, we’ve explored the new features of match expressions that make them even more powerful in Scala 3 applications, such as nested matches and use in if conditions. We’re also aware of syntactical changes between Scala 2 and Scala 3 and are know of the changes that need to be made when upgrading Scala 2 code to Scala 3.

As always, the sample code used in this article is available over on GitHub.