Scala Lesson 40 – Scala 3 Features | Dataplexa

Scala 3 Features

In this lesson, you will explore Scala 3 (also known as Dotty) and its most important new features. Scala 3 is a major evolution of the language, focused on better readability, stronger type safety, and simpler syntax.

Scala 3 keeps the power of Scala while reducing complexity and making code easier to understand, maintain, and scale.


Why Scala 3?

Scala 2 was powerful but sometimes difficult to learn and read. Scala 3 was designed to:

  • Simplify syntax
  • Improve compiler error messages
  • Strengthen the type system
  • Unify functional and object-oriented programming

Most Scala 2 code can be migrated to Scala 3 with minimal changes.


New Syntax with Significant Indentation

Scala 3 introduces indentation-based syntax, similar to Python, making code cleaner and less cluttered.

Scala 2 Style

if (x > 0) {
  println("Positive")
} else {
  println("Negative")
}

Scala 3 Style

if x > 0 then
  println("Positive")
else
  println("Negative")

Braces are optional, making code easier to read.


Optional then and do Keywords

Scala 3 adds keywords like then and do to clarify control flow.

while x < 5 do
  println(x)
  x += 1

This improves readability, especially for beginners.


Enums (Much More Powerful)

Scala 3 introduces a new enum construct that replaces many use cases of sealed traits.

enum Direction:
  case North, South, East, West

Enums can also contain fields and methods.

enum Status(code: Int):
  case Success extends Status(200)
  case NotFound extends Status(404)

Union Types

Scala 3 introduces union types using the | operator.

This allows a value to be one of multiple types.

def printValue(value: Int | String): Unit =
  println(value)

Union types reduce the need for inheritance or wrapper classes.


Intersection Types

Intersection types use the & operator. They require a value to satisfy multiple types at once.

trait A:
  def a(): Unit

trait B:
  def b(): Unit

def useBoth(x: A & B): Unit =
  x.a()
  x.b()

Given and Using (Replaces Implicits)

Scala 3 replaces implicit with clearer keywords: given and using.

given intOrdering: Ordering[Int] with
  def compare(a: Int, b: Int): Int = a - b

def sortNumbers(nums: List[Int])(using Ordering[Int]) =
  nums.sorted

This makes implicit behavior more explicit and readable.


Opaque Types

Opaque types allow you to create type-safe aliases without runtime overhead.

object UserId:
  opaque type UserId = Int
  def apply(id: Int): UserId = id

This improves safety without sacrificing performance.


Improved Type Inference

Scala 3 has a more powerful type inference engine, reducing the need for explicit type annotations.

Code becomes shorter while remaining safe and expressive.


Better Error Messages

One of the biggest improvements in Scala 3 is human-friendly compiler errors.

Error messages are more descriptive and actionable, making debugging easier.


Migration from Scala 2

Scala provides tools like scala3-migrate to help move existing Scala 2 code to Scala 3.

Most Scala 2 concepts still work, with cleaner alternatives available.


📝 Practice Exercises


Exercise 1

Rewrite a Scala 2 if-else statement using Scala 3 syntax.

Exercise 2

Create an enum with at least three values.

Exercise 3

Write a function using a union type.


✅ Practice Answers


Answer 1

if n % 2 == 0 then
  println("Even")
else
  println("Odd")

Answer 2

enum Color:
  case Red, Green, Blue

Answer 3

def show(x: Int | Double): Unit =
  println(x)

What’s Next?

In the next lesson, you will learn about Concurrency Basics in Scala, including threads, execution contexts, and parallel computation.