Since I created Shadchen, I've been lucky enough that the following, sometimes related, things have happened:
- I got a job programming in Common Lisp full time.
- I got married.
- I am/will be moving to France.
Understandably, this has resulted in a significant decrease in the frequency with which I have written things for this blog. Sorry - if anyone was itching for updates.
Shadchen is a pattern matching library which I've been developing
simultaneously in Emacs and Common Lisp - an interesting experience.
Shadchen has been a big hit at my new company, and you might imagine
that this would mean that the Common Lisp version of the library would
be developing rapidly while the Emacs Lisp version would struggle to
catch up. This turns out not to be the case for business process
shadchen.lisp is now "work" and so must compete
with other work related responsibilities, whereas
primarily subserves my personal projects, many of which still live in
As a consequence, it has long been possible to write:
(defun-match- product ((list (tail tl))) (recur tl 1)) (defun-match product ((list) acc) acc) (defun-match product ((list (must-match (number hd)) (tail tl)) acc) (recur tl (* acc hd)))
shadchen.el, to define a dynamically type checked tail
recursive product function. In fact, this system has been torn down
and rebuilt at least once in
shadchen.el but never appeared in
Common Lisp's version of the library1.
I've finally gotten around to implementing some of these new features
shadchen.lisp. Because most (all?) Common Lisp implementations
support tail call optimization in some form, and because supporting
recur in Emacs Lisp Shadchen is a considerably complex problem,
shadchen.lisp depends on the implementation's recursion, so the
above would be written:
(defun-match- product ((list (tail tl))) (product tl 1)) (defun-match ((list) acc) acc) (defun-match product ((list (must-match (number hd)) (tail tl)) acc) (product tl (* acc hd)))
In both Emacs Lisp and Common Lisp, one may write a
pattern, as either
Which, when encountered in the course of regular matching produces an
error if it fails to match. Generally, this is only useful when you
want to ensure that when one particular match succeeds and another
fails, that you find out about it immediately. In the above
function, if you pass a list like
(1 2 a 3 4) you will get an error
to the effect of
"must-match pattern ((number hd)) failed against: a"
must-match, the error would be the less informative
failed for product: (list a 3 4) 2 (or something like it).
Sometimes you want an even nicer error message, in which case you can write:
(! (number x) error-value (format nil "product only works on lists of numbers, but encountered ~S" error-value))
In this form, if the pattern fails, the failed value is bound to
error-value and then the final form is evaluated to produce a value
to pass to
error, in a lexical context with
error-value. You can
use any shadchen pattern for
error-value but since its an error
condition, you usually don't know what to expect.
One can also match against the tail of a list using the
(list a b c (tail some-tail-pattern))
This is identical to the syntax
(list-rest a b c some-tail-pattern)
defun-match argument lists are syntactically identical to a
list match, you can use
tail to match against the tail the
arguments passed in.
In the Emacs Lisp version of the library only, you can match against vectors using Emacs Lisp's vector syntax, eg:
(match [1 2 3] ([a b c] (list a b c)))
(1 2 3). One can use the
tail pattern in a vector to
capture the end of a vector, but in that case the tail value will be
coerced to a list.
Common Lisp users must match using
Perhaps not intuitively, I think of
shadchen.el as significantly
better stress tested than
shadchen.lisp. This has to do with the
fact that I've used the Emacs Lisp version to implement several large
projects (including this Lisp to Matlab
transcoder) and that Emacs
Lisp has simpler semantics. In particular, Common Lisp shadchen has
some problems with
multiple-values, which I am working on resolving.
On the other hand, its much better to have pattern matching than not to have it.
1 Another issue is that CLOS and
defun-match are not
really orthogonal language features, and so it was less necessary and
useful to have
defun-match in Common Lisp.