Switch Chains

Separating decisions from actions

by Marcel Garus · 2025-10-28 · available at www.marcelgarus.dev/switch-chains

My programming language Plum has postfix syntax for switching on enums:

a_bool
% true -> 2
  false -> 3

It also has structural typing, enum literals like | variant, and infers types of expressions:

value =
  a_bool
  % true -> | foo: 2
    false -> | bar: "Hi"

Here, the type of | foo: 2 is | foo: Int and the type of | bar: "Hi" is | bar: String. The type of a switch expression is the union of its cases, so | foo: Int bar: String (which you can read as "either it's foo with an Int payload, or bar with a String payload").

Turns out, structural typing and postfix switches compose really well!

In other languages, I'd often write code like this:

if condition {
  // action 1
  ...
} else {
  var value = computation()
  if other_condition(value) {
    // action 2
    ...
  } else {
    // action 3
    ...
  }
}

Note how I interleave the information gathering / decision making with actually doing the action? In Plum, I can cleanly separate them using this pattern:

condition
% true -> | action_1
  false ->
    value = computation
    other_condition value
    % true -> | action_2
      false -> | action_3
% action_1 -> ...
  action_2 -> ...
  action_3 -> ...

Here, I construct a value of the type | action_1 action_2 action_3 and then immediately switch on it. The comments are gone! The code is now fully self-documenting.

The first complex data structure written in Plum was the standard library's hash map. It uses a typical implementation: A giant array stores the entries and you do linear searches to put entries into a free place and to find them. A key's hash determines where you start the search, so it works in an ammortized runtime of O(1).

The code for putting an item into the map uses the pattern I described above:

raw_put
  buckets: (Array (Bucket k v)) index: Int key: k value: v
  -> (Array (Bucket k v))
= index = index .mod (buckets.length)
  buckets .get index
  % filled    -> | keep_searching
    empty     -> | use_this_bucket
    tombstone -> | use_this_bucket
  % use_this_bucket -> buckets .set index (| filled: (& key value))
    keep_searching  -> raw_put buckets (index .+ 1) key value

You don't need to understand every detail, but the gist is that I'm creating an enum | keep_searching use_this_bucket, and then switch on it to do the corresponding action. Here's another function that uses switch chains:

raw_get_maybe
  buckets: (Array (Bucket k v)) index: Int key: k equals: (\ k k -> Bool)
  -> (Maybe v)
= index = index .mod (buckets.length)
  buckets .get index
  % filled: entry ->
      equals (entry.key) key
      % true  -> | found_it: entry.value
        false -> | keep_searching
    tombstone -> | keep_searching
    empty     -> | not_in_map
  % found_it: value -> | some: value
    not_in_map      -> | none
    keep_searching  -> buckets .raw_get_maybe (index .+ 1) key equals

Beautiful!