# Case Classes, Enums and ADTs — Scala

Source: https://www.geekswithgeeks.com/en/scala/o-adts

> Model data with case classes, enums and sealed hierarchies.

## Algebraic data types

**Case classes** are concise immutable data types: `case class Item(sku: String, qty: Int)` generates public `val` fields, `equals`/`hashCode` by value, a readable `toString`, a `copy` method for modified copies and pattern-matching support. **Enums** (Scala 3) define a fixed set of cases, either simple (`enum Status { case Placed, Paid, Shipped }`) or with parameters and methods, and they interoperate with Java enums when extending `java.lang.Enum`. Together they express **algebraic data types (ADTs)**: **product types** (a case class holds this *and* that) and **sum types** (an enum or sealed trait is this *or* that). For example, a payment is `Card(last4)` or `Upi(vpa)` or `Cash`. Because the set of cases is closed, the compiler warns about **non-exhaustive matches**, so adding a new case shows you every place to update. Modelling the domain precisely with ADTs makes **illegal states unrepresentable**: rather than a `status: String` with a nullable `trackingId`, use `Shipped(trackingId)` that only exists once shipped.

## Modelling payments and order states

Enums with data, exhaustive matches and copy.

```scala
enum Payment:
  case Card(last4: String)
  case Upi(vpa: String)
  case Cash

enum OrderState:
  case Placed
  case Paid(payment: Payment)
  case Shipped(payment: Payment, trackingId: String)
  case Cancelled(reason: String)

case class Order(id: String, totalPaise: Long, state: OrderState)

def describe(p: Payment): String = p match
  case Payment.Card(last4) => s"card ending $last4"
  case Payment.Upi(vpa)    => s"UPI $vpa"
  case Payment.Cash        => "cash on delivery"
  // removing a case here makes the compiler warn: match may not be exhaustive

val placed = Order("o-1", 49900, OrderState.Placed)
val paid = placed.copy(state = OrderState.Paid(Payment.Upi("asha@okbank")))
val shipped = paid.state match
  case OrderState.Paid(p) => paid.copy(state = OrderState.Shipped(p, "TRK123"))
  case _                  => paid

println(shipped)
println(placed == Order("o-1", 49900, OrderState.Placed))   // true: value equality
```

## values exists only for simple enums

`EnumName.values` and `valueOf` are generated only when every case is a simple singleton such as `enum Status { case Placed, Paid }`. Payment and OrderState have parameterised cases, so they do not get `values`.

**Quiz:** What does making a hierarchy sealed (or using an enum) enable?

- [ ] Faster runtime dispatch only
- [ ] Mutable fields
- [x] Compiler exhaustiveness checks in pattern matches, because all cases are known
- [ ] Multiple inheritance of classes

*Answer:* Compiler exhaustiveness checks in pattern matches, because all cases are known. A closed set of cases lets the compiler warn when a match misses one.
