Pattern Matching

The case/when expression in Chasm for matching atoms, enums, and values.

The case expression matches a value against a list of patterns and returns the result of the first matching arm.

Basic Syntax

case value do
  when :idle    -> "standing by"
  when :running -> "in motion"
  when :dead    -> "game over"
  _             -> "unknown state"
end

Arms are matched top to bottom. _ is the wildcard catch-all, it matches everything. If no arm matches and there is no _, the expression produces a zero value for its type.

case as an Expression

case returns a value. Assign it directly:

label = case @state do
  when :idle    -> "idle"
  when :running -> "run"
  _             -> "?"
end

Use it inside a string interpolation:

status = "State: #{case @gs do when GameState.Menu -> "menu" when GameState.Playing -> "game" _ -> "other" end}"

Matching Atoms

Atoms are the most common pattern target, they compare by identity so matching is fast:

defp color_for(state :: atom) :: int do
  case state do
    when :healthy  -> 0x00ff00ff
    when :hurt     -> 0xff8800ff
    when :critical -> 0xff0000ff
    _              -> 0xffffffff
  end
end

Matching Enum Values

enum Dir { North, South, East, West }

defp dx(d :: Dir) :: float do
  case d do
    when East  ->  1.0
    when West  -> -1.0
    _          ->  0.0
  end
end

defp dy(d :: Dir) :: float do
  case d do
    when South ->  1.0
    when North -> -1.0
    _          ->  0.0
  end
end

Matching Integers

case works on any comparable type, including int:

defp tier(score :: int) :: string do
  case score / 100 do
    when 0 -> "bronze"
    when 1 -> "silver"
    when 2 -> "gold"
    _      -> "legend"
  end
end

Nested case

result = case @phase do
  when :attack ->
    case @target_count do
      when 0 -> "no target"
      _      -> "attacking"
    end
  when :retreat -> "retreating"
  _             -> "idle"
end

Compared to if/else Chains

Use case when you are branching on a single value with many possible matches, it is cleaner than a long if / else if / else chain and more efficient when matching atoms or enum variants.

# Prefer case:
label = case @state do
  when :idle    -> "I"
  when :running -> "R"
  when :jumping -> "J"
  _             -> "?"
end

# Equivalent but harder to read:
label = if @state == :idle do "I" else if @state == :running do "R" else if @state == :jumping do "J" else "?" end end end