A Primer on Guile Match Expressions

Notes

  • Match expressions are useful for destructuring information
  • The match expression is not standard in Scheme, it’s based on a paper by Andrew K. Wright
  • The (ice-9 match) module in Guile is based on a match implementation by Alex Shinn

Examples

match

(use-modules (ice-9 match))

(match '(1 2 3)
  ((x y z)
   (format #f "y is ~a" y)))

(match '(1 (2 3) 4)
  ((x (y z) foo)
   (format #f "z is ~a" z)))

(match '(1 2 3)
  ((1 y z)
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(match '(3 2 1)
  ((1 y z)
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(match '(3 2 1)
  ((1 y z)
   (format #f "y is ~a" y))
  ((a b) ;; List length does not match!
   (format #f "b is ~a" b))
  (_ "Did not match!"))

(match '(3 2 1)
  ((1 y z)
   (format #f "y is ~a" y))
  ((a b c)
   (format #f "b is ~a" b))
  (_ "Did not match!"))

(match '(3 2 1)
  ((or (1 y z)
       (x y z))
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(match '(3 2 1)
  ((and (1 y z)
        (x y z))
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(match '(1 2 3)
  ((and (1 y z)
        (x y z))
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(match '(1 2 3)
  ((first . rest)
   (format #f "First item is ~a and rest is ~a." first rest))
  (_ "Did not match!"))

(let loop ((items '(1 2 3 4 5))
           (strings '()))
  (match items
    ((item . rest)
     (loop rest
           (cons (format #f "Item ~a" item)
                 strings)))
    (() (reverse strings))))

(let loop ((items '(1 2 3 4 5))
           (strings '()))
  (if (pair? items)
      (loop (cdr items)
            (cons (format #f "Item ~a" (car items))
                  strings))
      (reverse strings)))

(match '(1 2 3)
  ((numbers ...)
   (format #f "Here are some numbers: ~a" numbers))
  (_ "Did not match!"))

(match #(1 2 3)
  ((x y z)
   "It's a list!")
  (#(x y z)
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(use-modules (srfi srfi-9))

(define-record-type person
  (make-person name age hometown)
  person?
  (name person-name)
  (age person-age)
  (hometown person-hometown))

(match (make-person "David" 132 "Mars")
  (($ person name age hometown)
   (format #f "~a is ~a and from ~a." name age hometown))
  (_ "Not a record of 'person'!"))

(match (make-person "David" 132 "Mars")
  ((= person-age 50)
   "The person is 50 years old.")
  (_ "Not 50 years old!"))

(match (make-person "David" 50 "Mars")
  ((= person-age 50)
   "The person is 50 years old.")
  (_ "Not 50 years old!"))

(match (make-person "David" 50 "Mars")
  ((? person? person)
   (format #f "The person is ~a years old." (person-age person)))
  (_ "Not 50 years old!"))

match-lambda and match-lambda*

(match '(1 2 3)
  ((and (1 y z)
        (x y z))
   (format #f "y is ~a" y))
  (_ "Did not match!"))

(define match-numbers
  (match-lambda
    ((and (1 y z)
          (x y z))
     (format #f "y is ~a" y))
    (_ "Did not match!")))

(match-numbers '(1 2 3))
(match-numbers '(3 2 1))

(define match-numbers*
  (match-lambda*
    ((and (1 y z)
          (x y z))
     (format #f "y is ~a" y))
    (_ "Did not match!")))

(match-numbers* 1 2 3)
(match-numbers* 3 2 1)

match-let

(define my-values '(1 (1 2) 3))

(match-let (((x y z) my-values))
   (format #f "y is ~a" y))

(match-let (((x (a b) z) my-values))
   (format #f "b is ~a" b))

(match-let* (((x y z) my-values)
             ((a b) y))
   (format #f "b is ~a" b))

(match-let loop (((or (item . rest)
                      _)
                  '(1 2 3 4 5))
                 (strings '()))
  (if (null? item)
      strings
      (loop rest
            (cons (format #f "Item ~a" item)
                  strings))))

Also match-let* and match-letrec

Community-Contributed Examples

Contribute your own examples by submitting a pull request to the repository on Codeberg!

From @jotaemei:

match-lambda and match-lambda*

(define factorial
  (match-lambda
    ((or 0 1) 1)
    ((? number? n) (* n (factorial (- n 1))))
    (_ "Invalid input. It should be an integer.")))

(factorial 5)
-> 120

;; An alternative to the match-let loop above.

(define itemize
  (match-lambda
    ((? list? lst) (reverse (itemizer lst '())))
    (_ "Invalid input. It should be a list.")))

(define itemizer
  (match-lambda*
    ([() acc] acc)
    ([(head . tail) acc]
     (itemizer tail (cons (format #f "Item ~a" head) acc)))))

(itemize '(1 2 3 4 5))
-> ("Item 1" "Item 2" "Item 3" "Item 4" "Item 5")