Simplifying SCALA 3 features

Amit Prasad
4 min readApr 13, 2024

Scala 3 aka Dotty is a major release version of Scala programming language.

This version introduces the power features of Scala so far if we compare it with its version 2. x.

We are going to discuss the following in detail:

  • Optional Braces
  • Union Types
  • Intersection Types
  • Trait Parameters
  • Enumeration

We have had the practice of using {...} inside the brackets, we needed to indicate the code block within the block. Scala 3 gives us a provision where we can write it with indentation and it can be considered the total code block within that indentation much like (the Python style of coding).

Scala 2.x style

val age = 18
val eligibleForVote = if( age >= 18) {
"eligible"
} else {
"not eligible"
}

even more compact

val eligibleForVote = if(age >= 18) "eligible" else "not eligible"

scala 3 (Dotty version) has introduced one more keyword then to simplify the block of code, especially for if-else

val eligibleForVote = 
if(age >= 18) then
"eligible"
else
"not eligible"

between then and else we can consider the code block which can be evaluated for if block.

Another example of option braces can be for comprehension

Scala 2.x version

val forCompExample = for {
ids <- List(1,2,3,4,5)
names <- List("Robert","Mike", "John", "Peter", "Jacob")
} yield s"$ids $names"

Scala 3 Cleaner version

val forCompExampleV2 = 
for
ids <- List(1,2,3,4,5)
names <- List("Robert","Mike", "John", "Peter", "Jacob")
yield s"$ids $names"

Similarly, it can apply to match , methods also.

Except omitting the {...} , we can see some examples where we can use : also to point out the beginning of the block for class , traits etc.

trait A: // points that it starts the block for traits
def f: Int

And if we want to implement the same trait A: we can simply use the below code:

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

Likewise, we can use it for package, object , enum , anonymous classetc.

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

Union Types

There are new types defined in the dotty version, one of them is the Union types.

A union type A | B includes all values of both types.

  trait Detail
case class ID(id:Int) extends Detail
case class Name(name:String) extends Detail

def help(detail: ID | Name) =
val user = detail match
case ID(id) => lookupID(id)
case Name(name) => lookupName(name)

Union types are duals of intersection types, meaning A|B is same as B|A .

The compiler assigns a union type to an expression under two conditions:

  • When the type is specified explicitly
  • When common supertypes of all alternatives is transparent

Examples:

scala> val id = ID(123)
val id: ID = ID(123)

scala> val name = Name("Jacob")
val name: Name = Name(Jacob)

scala> if true then name else id
val res1: Name = Name(Jacob)

scala> val either: ID | Name = if true then name else ID
val either: Name | ID = Name(Jacob)

You can change inference behavior using transparent keyword

transparent trait Detail
scala> if true then name else id
val res2: Name | ID = Name(Jacob)

Intersection Types

Similar to Union types it also operates on type only and the operator we use is & .

The type A & B represents values that are of the type Aand Bat the same time.

Examples

trait Order:
def placeOrder(): Unit

trait Invoice:
def printInvoice(): Unit

def f(x: Order & Invoice) =
x.placeOrder()
x.printInvoice()

Trait Parameters

We can now pass the parameters to traits the same as class and case class.

trait Greeting(val name: String):
def msg = s"How are you, $name"

class C extends Greeting("Bob"):
println(msg)

You can read this in detail here on how to avoid ambiguity for multiple extents.

Enumeration

Until the Scala 2. x version we used to create the enums using either an Enumeration class (much like Java style) or using a case class. It had several challenges when using it for pattern match for exhaustive search, and also it used to have NoSuchElementException due to withName API of scala.Enumeration .

scala 2.x example

object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._

def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)

WeekDay.values filter isWorkingDay foreach println

Scala 3 has a cleaner approach to creating and using the enum

enum Permission:
case Read, Write, Execute

This defines a new sealed class.

We can access each value like Permission.Read , Permission.Write , Permission,Execute .

All the values are part of the companion object.

Passing Param to enum

Enum can be parameterized

enum Permission(val value: Int):
case Read extends Permission(1)
case Write extends Permission(2)
case Execute extends Permission(3)

References:

https://docs.scala-lang.org/scala3/reference

--

--

Amit Prasad

Engineer by profession, Scala | Data engineering | Distributed System Linkedin: https://www.linkedin.com/in/amitprasad119/