MatchUtils

Utility macros built on top of match. Provides let-match for pattern bindings with explicit fallback, and defn-match for pattern-matching function definitions.

defn-match

macro

Macro

                        (defn-match name :rest forms)
                    

Defines a pattern-matching function. Each row is a list of patterns followed by a body. Dispatch is compiled to nested match and case forms; columns are scored and the most discriminating positions are tested first.

(deftype Num
  (I [Int])
  (D [Double]))

(MatchUtils.defn-match add-num
  [(Num.I x) (Num.I y)] (Num.I (+ x y))
  [(Num.D x) (Num.D y)] (Num.D (+ x y))
  [(Num.D x) (Num.I y)] (Num.D (+ x (from-int y)))
  [(Num.I x) (Num.D y)] (Num.D (+ (from-int x) y)))

When all rows have the same arity, expands to a single defn with nested match dispatch. add-num above is a plain function, first-class and usable in higher-order contexts.

When rows have differing arities, expands to one defn per arity named name-0, name-1, …, plus a dispatching macro name that picks the right arity at the call site:

(MatchUtils.defn-match count
  []                         0
  [(Maybe.Just _)]           1
  [(Maybe.Just _) (Maybe.Just _)] 2)

; call sites: (count), (count m), (count m1 m2)
; first-class per arity: count-0, count-1, count-2
;   (Array.map count-1 maybes)   -- works
;   (Array.map count m)          -- does NOT work, count is a macro

A row with a variable binding at column k will shadow later rows that have concrete patterns at the same column, since the variable matches anything at that position. Write concrete rows first and catch-alls last.

When invoked as a direct defn body, prefer the qualified form MatchUtils.defn-match over (use MatchUtils) for the same reason documented on let-match.

let-match

macro

Macro

                        (let-match bindings then else)
                    

Pattern-binding form with an explicit fallback. Tries each pattern expression pair in bindings; if every pattern matches, evaluates then with the bindings in scope. If any pattern fails, evaluates else. Both branches must have the same type, so the form is well-typed regardless of body type.

(MatchUtils.let-match [(Maybe.Just x) (Array.nth &xs 0)]
  (IO.println &(fmt "got %d" x))
  (IO.println "empty"))

When invoked as a direct defn body, prefer the qualified form MatchUtils.let-match over (use MatchUtils). A Carp quirk causes unqualified use-imported macros expanding to match to leak the pattern variable into value-lookup.