Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Example: ADTs + match

This example shows the usual pattern:

  1. define an ADT
  2. consume it with match

Goal

Build a tiny “Maybe” API:

  • fromMaybe (extract with default)
  • mapMaybe (transform inside Just)
  • isJust (check which constructor you have)

Define an ADT

type Maybe a = Just a | Nothing;

Use it

type Maybe a = Just a | Nothing;

let
  fromMaybe = \d m ->
    match m with {
      case Just x -> x;
      case Nothing -> d;
    }
in
  ( fromMaybe 0 (Just 5)
  , fromMaybe 0 Nothing
  )

Next steps

The next example implements mapMaybe, which applies a function to Just x and leaves Nothing unchanged.

A worked mapMaybe

type Maybe a = Just a | Nothing;

let
  mapMaybe = \f m ->
    match m with {
      case Just x -> Just (f x);
      case Nothing -> Nothing;
    }
in
  ( mapMaybe ((+) 1) (Just 41)
  , mapMaybe ((+) 1) Nothing
  )

Testing constructors with match

There’s no special “isJust” operator — you write it with match:

type Maybe a = Just a | Nothing;

let
  isJust = \m ->
    match m with {
      case Just _ -> true;
      case Nothing -> false;
    }
in
  (isJust (Just 1), isJust Nothing)

Common mistake: missing arms

When matching on an ADT, Rex checks exhaustiveness. If you forget an arm, you’ll get an error that names the missing constructors.

Worked examples

Example: orElse

Problem: return the first Just value, otherwise return the fallback Maybe.

type Maybe a = Just a | Nothing;

let
  orElse = \ma mb ->
    match ma with {
      case Just x -> Just x;
      case Nothing -> mb;
    }
in
  (orElse (Just 1) (Just 2), orElse Nothing (Just 2))

Why this works: match chooses ma when it is Just, and only uses mb when ma is Nothing.

Example: andThen (Maybe bind)

Problem: chain a function that returns Maybe, failing early on Nothing.

type Maybe a = Just a | Nothing;

let
  andThen = \f ma ->
    match ma with {
      case Just x -> f x;
      case Nothing -> Nothing;
    },
  step = \x -> if x > 0 then Just (x + 1) else Nothing
in
  (andThen step (Just 3), andThen step Nothing, andThen step (Just (0 - 1)))

Why this works: Just unwraps and continues with f; Nothing short-circuits immediately.

Example: adding Unknown and handling exhaustiveness

Problem: extend Maybe and still keep matches exhaustive.

type Maybe a = Just a | Nothing | Unknown;

let
  fromMaybe = \d m ->
    match m with {
      case Just x -> x;
      case Nothing -> d;
      case Unknown -> d;
    }
in
  (fromMaybe 0 (Just 5), fromMaybe 0 Unknown)

Why this works: the Unknown arm makes the match complete for all constructors.