Scala 3 · 41 of 42

Union & intersection types

Two new kinds of types arrived in Scala 3, both straight from type theory:

Union types: A | B is a value that is either an A or a B. Before Scala 3 you’d reach for Either[A, B] (with wrapping/unwrapping) or a common supertype (losing precision). A union is exact and requires no wrapper: findUser above returns a real String or a real NotFound, and pattern matching takes them apart. Unions also power safer Java interop: under the -Yexplicit-nulls flag, a nullable Java String becomes String | Null, making null-handling visible in the types.

Intersection types: A & B is a value that is both an A and a B. It replaces Scala 2’s A with B in type positions, with a cleaner property: A & B and B & A are the same type. You saw one in this tour’s overview page: val person: Namable & Randomable.

Both are structural glue: you’ll use unions frequently in application code, and meet intersections mostly in APIs that combine capabilities.

Note: also new in the type department (worth a look once these feel comfortable): opaque types for zero-cost wrappers, and match types for type-level computation.
//union types: a value that is one of several types
def describe(x: Int | String): String = x match
  case n: Int    => s"number $n"
  case s: String => s"text '$s'"

println(describe(42))
println(describe("hi"))

//great for precise error handling without exceptions
case class NotFound(id: Int)

def findUser(id: Int): String | NotFound =
  if id == 1 then "Alice" else NotFound(id)

findUser(1) match
  case name: String  => println(s"found $name")
  case NotFound(id)  => println(s"no user $id")

//intersection types: a value that is several types at once
trait Named { def name: String }
trait Aged { def age: Int }

def intro(x: Named & Aged) = s"${x.name}, ${x.age} years old"

case class Person(name: String, age: Int) extends Named, Aged

println(intro(Person("Alice", 30)))

Contents

Scala basics

  1. Overview
  2. Scalculator
  3. Operators are methods
  4. Variables
  5. Final variables
  6. Printing values
  7. String interpolation
  8. String formatting
  9. Useful operations
  10. Method definition
  11. Method definition 2
  12. Method definition 3
  13. Anonymous functions
  14. Anonymous functions 2
  15. Return multiple values
  16. Declare multiple variables
  17. Assign multiple variables
  18. Loops using while
  19. Loops using for
  20. Loops without loops
  21. If
  22. Match as a switch
  23. Arrays
  24. Lists
  25. Sets
  26. Maps
  27. Mutable collections
  28. Collections: accessing elements
  29. Collections: concatenation
  30. Mutable collection operations
  31. Immutable collections with var
  32. Collections: useful methods
  33. Classes
  34. Classes, continued

What's new in Scala 3

  1. Scala 3: what changed
  2. Optional braces & new control syntax
  3. Top-level definitions & @main
  4. Enums & ADTs
  5. Extension methods
  6. given & using
  7. Union & intersection types
  8. Smaller niceties