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 News
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
reasons: developing shadchen.lisp is now "work" and so must compete
with other work related responsibilities, whereas shadchen.el
primarily subserves my personal projects, many of which still live in
Emacs Lisp.
So, defun-match
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)))
Using 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
in 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)))
New Patterns
In both Emacs Lisp and Common Lisp, one may write a must-match
pattern, as either
(must-match ...)
or
(! ...)
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 product
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"
Without must-match, the error would be the less informative match
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 pattern
like so:
(list a b c (tail some-tail-pattern))
This is identical to the syntax
(list-rest a b c some-tail-pattern)
Since defun-match argument lists are syntactically identical to a
list match, you can use tail to match against the tail the
arguments passed in.
Vectors
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)))
Evaluates to (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 vector.
Notes
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.