Ongoing adventures with lisp
Can you imagine indenting code as follows in python?
i=0, j=1
k, l = 34, 65
[i, k] = [k, i]
Each of these assignments corresponds to a different special form in Common
Lisp:
(let* ((i 0)
(j 1))
(multiple-value-bind (k l) (values 34 65)
(destructuring-bind (i k) '(k i)
...)))
Verbose and visually cluttered. When I realized returning multiple values was
turning into such a pain it was turning me off values, I created the
bind macro in response:
(bind (i 0)
(j 1)
(:mv (k l) (values 34 65))
(:db (i k) '(k i))
:do
...)
Much better. Here's how I implement bind:
(defmacro bind (&rest body)
(apply 'bind-func body))
(defun bind-func (&rest body)
(cond ((null (first body)) (error "no :do in bind"))
((eq (first body) :do) `(progn ,@(rest body)))
(t (multiple-value-bind (let-form body)
(determine-let-form body)
(destructuring-bind ((a b) . body) body
(if (eq let-form 'let)
`(let ((,a ,b)) ,(apply 'bind-func body))
`(,let-form ,a ,b ,(apply 'bind-func body))))))))
(defun determine-let-form (form)
(destructuring-bind ((a . rest) . body) form
(cond ((eq a :db) (values 'destructuring-bind (cons rest body)))
((eq a :mv) (values 'multiple-value-bind (cons rest body)))
(t (values 'let form)))))
Within lisp buffers in emacs I navigate entirely in
terms of S-expressions. C-Left and C-Right move 'horizontally' in the parse
tree to the previous or next sibling, or up the tree if that's not possible.
C-Up and C-Down move 'vertically' in the parse tree to the parent or first
child of the sexp under point. Here's how these intuitive keymappings are
implemented in my .emacs:
(defun backward-or-up-sexp ()
(interactive)
(if (eq (char-before (point)) ?\()
(backward-up-list)
(backward-sexp)))
(defun forward-or-up-sexp ()
(interactive)
(if (eq (char-after (point)) ?\))
(up-list)
(forward-sexp)))
(defun down-sexp ()
(interactive)
(if (member (char-after (point)) '(?\n ?\)))
(backward-sexp))
(down-list))
(global-set-key '(control left) 'backward-or-up-sexp)
(global-set-key '(control right) 'forward-or-up-sexp)
(global-set-key '(control up) 'backward-up-list)
(global-set-key '(control down) 'down-sexp)
...
footnotes
0. I prefer to use keywords rather than
argument index+indentation to highlight semantic structure.
1. For two syntactic alternatives that
don't use the
:db and
:mv keywords, see
Gary King's version, coincidentally also
called bind.
Last updated some time in 2004