An advanced textbook on Common Lisp, with special focus on macros. Highly recommended. Currently out of print. Paul Graham has also gotten the copyright back and has made it freely available (both PDF and Postscript) on his website. A couple of unofficial TexInfo versions exist as well. The source code for the examples is also available—both from the official site and as PgUtils.
On Lisp (official site) | http://www.paulgraham.com/onlisp.html (PDF, PostScript, Errata |
TexInfo (unofficial version by Shawn Betts) | http://mirror.lug.udel.edu/pub/fink/distfiles/onlisp.texi.gz |
HTML (based on unofficial texinfo version) | http://www.bookshelf.jp/texi/onlisp/onlisp.html |
epub | https://bitbucket.org/mbishop/on-lisp |
PDF (with LaTeX sources) | https://bitbucket.org/blevyq/onlisp |
When defining continuation-passing macros (p. 267) Paul Graham assumes that *cont* is a global with lexical scope (p. 268):
It is by manipulating *cont* that we will get the effect of continuations. Although *cont* has a global value, this will rarely be the one used: *cont* will nearly always be a parameter, captured by =values and the macros defined by =defun. Within the body of add1, for example, *cont* is a parameter and not the global variable. This distinction is important because these macros wouldn't work if *cont* were not a local variable. That’s why *cont* is given its initial value in a setq instead of a defvar: the latter would also proclaim it to be special.
The Common Lisp standard, however, does not specify global lexical variables, and the behavior of setq with an undefined variable has undefined consequences. With present day Common Lisp implementations, the aforementioned macros just don't work. Also, this issue can be very confusing for newcomers. Suggested solutions for fixing the macros are (note that #'values is used instead of #'identity - according to Paul Graham's Errata):
- emulate lexically scoped global variable *cont* using symbol-macro that can be shadowed by let or lambda:
(defvar *actual-cont* #'values)
(define-symbol-macro *cont* *actual-cont*) - just omit (setq *cont* #'identity) and call "top-level" continuation-passing function as (=somefunc #'values ...)
- ...