1. Overview

One of the new features introduced in Scala 3 was the possibility of suppressing some occurrences of braces.

2. Optional Braces

Some braces can be replaced by an enforced indentation. Furthermore, poorly indented programs are flagged with warnings. This is called significant indentation, and it’s enabled by default. Using the compiler flag -no-indent, we can disable the feature.

2.1. Indentation Rules

There are two rules for well-indented programs. If either of those is not respected, the compiler will emit a warning. First, in a brace-delimited region, no statement is allowed to start to the left of the first statement after the opening brace that starts a new line:

if (x < 0) {
  println(1)
  println(2)

println("done")  // error: indented too far to the left

Second, if significant indentation is turned off and we are at the start of an indented sub-part of an expression, and the indented part ends in a newline, then the next statement must start at an indentation width less than the sub-part:

if (x < 0)
  println(1)
  println(2)   // error: missing `{`

These rules are not very strict on purpose. Their goal is to be helpful in pinpointing the root causes of errors related to missing braces.

2.2. Optional Braces for Class Definition

When defining a new class, trait, or object, we can also omit braces by using a colon (‘:’). The following are now valid definitions in Scala 3:

trait A:
  def f: Int

class C(x: Int) extends A:
  def f = x

object O:
  def f = 3

enum Color:
  case Red, Green, Blue

new A:
  def f = 3

package p:
  def a = 1

package q:
  def b = 2

2.3. Special Treatment of case Clauses

Scala 3 also introduced some changes to the syntax of match/case clauses. Prior to Scala 3, when using pattern matching, we’d need to use braces:

import scala.util.Random

val x: Int = Random.nextInt(10)

x match {
  case 0 => "zero"
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}

But in Scala 3, we can now rewrite without braces:

import scala.util.Random

val x: Int = Random.nextInt(10)

x match
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case _ => "other"

2.4. The end Marker

One of the potential issues raised when discussing this new feature is that it can make the code more difficult to understand when a given block ends. To solve this problem, Scala 3 offers an optional end marker:

def largeMethod(...) =
  ...
  if ... then ...
  else
    ... // a large block
  end if
  ... // more code
end largeMethod

The end marker consists of the keyword end together with a specifier token. The specifier token can be one of the following:

  • if
  • while
  • for
  • match
  • try
  • new
  • this
  • val
  • given
  • <identifiers>

3. Conclusion

In this article, we saw one of the many new features introduced in Scala 3, optional braces. This language feature allows replacing some usages of braces by following specific indentation rules.

Comments are closed on this article!