Wednesday, November 28, 2012

More Updates to Shadchen

Since I created Shadchen, I've been lucky enough that the following, sometimes related, things have happened:

  1. I got a job programming in Common Lisp full time.
  2. I got married.
  3. 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.

  • The Emacs Lisp shadchen is available here.
  • The Common Lisp shadchen is available here.

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.