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.