[racket] One define to rule them all
In internal definition contexts, I've been using the following macro to
define everything but functions:
(define-syntax (def stx)
(syntax-case stx (values)
[(_ x e0 e ...)
(identifier? #'x)
(syntax/loc stx
(define x (let () e0 e ...)))]
[(_ (values x ...) e0 e ...)
(andmap identifier? (syntax->list #'(x ...)))
(syntax/loc stx
(define-values (x ...) (let () e0 e ...)))]
[(_ (values pat ...) e0 e ...)
(syntax/loc stx
(match-define (list pat ...)
(call-with-values (lambda () e0 e ...) list)))]
[(_ pat e0 e ...)
(syntax/loc stx
(match-define pat (let () e0 e ...)))]))
So these do the obvious things:
(def a 4)
(def (values) (values))
(def (values b c) (values 4 5))
(def (values (list d e) f)
(values (list 4 5) 6))
Common cases like (def x:id e ...+) and (def (values x:id ...) e ...+)
expand to the most direct define-like form. (It avoids `match-define'
because `match-define' 1) always binds a failure thunk; and 2) forces
multiple values to be converted to lists.)
With `def', it's easier to recognize a sequence of definitions leading
up to a computation buried inside a conditional branch. For example,
instead of:
(cond ...
[(the-condition? z-sig)
(define x (compute-x z))
(match-define (foo y-bar y-baz) y)
(define-values (a b) (get-some-values x y-bar))
(the-computation x y-baz a b)] ...)
I have:
(cond ...
[(the-condition? z-sig)
(def x (compute-x z))
(def (foo y-bar y-baz) y)
(def (values a b) (get-some-values x y-bar))
(the-computation x y-baz a b)] ...)
It's hard to miss all the `def's lined up in the same column.
Have I left anything out? Does anybody else think it's awesome/useful?
Hideous?
Neil T
(It's a shame it can't do functions, but there's no way to tell whether
(def (foo y-bar y-baz) ...) is intended to match a foo struct or define
a foo function. Also, optional parameters and struct matching look the
same. I wrote a `defun' macro that solves the latter problem - it even
does currying and keywords - but I can't say that I like it yet.)