# Enums and pattern matching

> Model alternatives with enums and sum types, destructure values, and handle exhaustiveness.

Canonical: https://alice-lang.org/documentation/language/pattern-matching/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

## Enums with data

An enum can describe named alternatives with different payloads. Construct a variant with `Type::Variant(arguments)` and destructure it in a `case`.

```alice
enum Shape
  Circle(r : Float64)
  Rect(w : Float64, h : Float64)
  Empty
end
def area(s : Shape) : Float64
  case s
  when Shape::Circle(r) then 3.0 * r * r
  when Shape::Rect(w, h) then w * h
  when Shape::Empty then 0.0
  end
end
puts area(Shape::Circle(2.0))
puts area(Shape::Rect(3.0, 4.0))
puts area(Shape::Empty)
```

Expected output:

```text
12
12
0
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.ZW51bSBTaGFwZQogIENpcmNsZShyIDogRmxvYXQ2NCkKICBSZWN0KHcgOiBGbG9hdDY0LCBoIDogRmxvYXQ2NCkKICBFbXB0eQplbmQKZGVmIGFyZWEocyA6IFNoYXBlKSA6IEZsb2F0NjQKICBjYXNlIHMKICB3aGVuIFNoYXBlOjpDaXJjbGUocikgdGhlbiAzLjAgKiByICogcgogIHdoZW4gU2hhcGU6OlJlY3QodywgaCkgdGhlbiB3ICogaAogIHdoZW4gU2hhcGU6OkVtcHR5IHRoZW4gMC4wCiAgZW5kCmVuZApwdXRzIGFyZWEoU2hhcGU6OkNpcmNsZSgyLjApKQpwdXRzIGFyZWEoU2hhcGU6OlJlY3QoMy4wLCA0LjApKQpwdXRzIGFyZWEoU2hhcGU6OkVtcHR5KQ)

`Circle` carries one number, `Rect` two, and `Empty` none. A variant name is part of its type's closed set of alternatives. Two variants with equal payloads have value equality.

The method's case covers every variant. Leaving one out without an `else` is a compile error (AL0201). Plain enums without payloads receive the same exhaustiveness checks.

## Array patterns

`case ... in` supports Ruby-style array destructuring:

```alice
def describe(xs : Array(Int32)) : String
  case xs
  in [] then "empty"
  in [x] then "one: #{x}"
  in [first, *rest] then "first: #{first}, remaining: #{rest.size}"
  end
end
puts describe(Array(Int32).new(0))
puts describe([7])
puts describe([1, 2, 3])
```

Expected output:

```text
empty
one: 7
first: 1, remaining: 2
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.ZGVmIGRlc2NyaWJlKHhzIDogQXJyYXkoSW50MzIpKSA6IFN0cmluZwogIGNhc2UgeHMKICBpbiBbXSB0aGVuICJlbXB0eSIKICBpbiBbeF0gdGhlbiAib25lOiAje3h9IgogIGluIFtmaXJzdCwgKnJlc3RdIHRoZW4gImZpcnN0OiAje2ZpcnN0fSwgcmVtYWluaW5nOiAje3Jlc3Quc2l6ZX0iCiAgZW5kCmVuZApwdXRzIGRlc2NyaWJlKEFycmF5KEludDMyKS5uZXcoMCkpCnB1dHMgZGVzY3JpYmUoWzddKQpwdXRzIGRlc2NyaWJlKFsxLCAyLCAzXSk)

`[]` matches an empty array, `[x]` exactly one element, and `[first, *rest]` a nonempty array with a captured tail. Array length coverage matters for exhaustiveness; a rest pattern does not cover the shorter lengths excluded by its required elements.

## Bindings, alternatives, and guards

| Pattern                           | Meaning                                                    |
| --------------------------------- | ---------------------------------------------------------- |
| `in x`                            | Bind the whole subject to `x`                              |
| `in _`                            | Match anything                                             |
| `in ^existing`                    | Compare with an existing value rather than bind a new name |
| `in 1 \| 2 \| 3`                  | Match any listed alternative                               |
| `in Shape::Circle(r) if r > 10.0` | Destructure, then check a guard                            |

A guard can use names introduced by its pattern. A guarded branch does not cover every instance of that variant, so add an unguarded branch or a fallback.

```alice
def category(n : Int32) : String
  case n
  in 0 then "zero"
  in 1 | 2 | 3 then "small"
  in x if x < 0 then "negative"
  in _ then "other"
  end
end
puts category(-4)
```

## Hash patterns

`in {"lang" => "pt", "name" => n}` requires those keys, matches the language value, and binds the name. Other keys may remain. Use a final catch-all for a dictionary whose contents are not a closed enum.

```alice
def greet(h : Dictionary(String, String)) : String
  case h
  in {"lang" => "pt", "name" => n} then "Olá #{n}"
  in {"name" => n} then "Hello #{n}"
  in _ then "Hello, guest"
  end
end
puts greet({"name" => "Alice"})
```

## Choosing a fallback

An `else` or catch-all is appropriate when unrecognized input is expected. For a closed domain you own, explicit branches help the compiler find callers that need updating when a variant is added. Do not add a catch-all merely to silence a useful exhaustiveness error.

## Continue reading

- Previous: [Generics and constraints](https://alice-lang.org/documentation/language/generics.md)
- Next: [Exceptions and cleanup](https://alice-lang.org/documentation/language/exceptions.md)
