Idiomdrottning’s homepage

match-generics

This post is a historical record because pretty soon after, this library instead evolved into the fanciest define of all time.

So go look at that instead!

This morning we made the fastest generics of all time. Now, let’s make the most flexible ones! They’re almost as fast, too.

They don’t need to have the same number of arguments or use the same names:

(define-generic (frotz a b c) (list b a c))
(define-generic (frotz x y) (+ x y))
(list (frotz 1 2 3) (frotz 4 5))

⇒ ((2 1 3) 9)

They can even destructure their arguments:

(define-generic (my-map proc (x . xs))
  (cons (proc x) (my-map proc xs)))

(define-generic (my-map proc ()) '())

(my-map add1 '(1 2 3))

⇒ (2 3 4)

To use predicates, use the form (? pred var), like this:

(define-generic (plus (? list? a) (? list? b)) (append a b))

(define-generic (plus (? string? a) (? string? b)) (string-append a b))

(define-generic (plus (? number? a) (? number? b)) (+ a b))

(list
 (plus "13" "14")
 (plus 7 22)
 (plus 13 19)
 (plus '(a b c) '(1 2 3)))

⇒ (“1314” 29 32 (a b c 1 2 3))

You need to define them in order from least specific to most specific, i.e. define the most specifics last and the fallbacks first.

You can even use nested defines! But if you do, each need to use the same inner forms. Only the outer-most form is generic.

(define-generic ((frobnicate a) b z) (list a b z))
(define-generic ((frobnicate a) b) (string-append a b))

(let ((hoho (frobnicate "hoho")))
  (list (hoho 1 2)
        (hoho " and such")))

⇒ ((“hoho” 1 2) “hoho and such”)

Implementation

By now, maybe some of the long-time readers in the audience has guessed what is going on.

Each generic is defined using a clause from matchable!

The implementation using some miscellaneous brev-separate stuff is just… five lines!

(define-for-syntax gentable (call-table))
(define-ir-syntax
  (define-generic (name . pattern) . body)
  (cons 'match-define
        (gentable (strip-syntax name) (cons (cons name pattern) body))))