5 ms·
> In Lisp, source filters never break. > Never. To be perfectly fair, this is not quite true. In Common Lisp (the dialect being used here), macros can break i
by sjolsen 12y ago
> In Lisp, source filters never break.
> Never.
To be perfectly fair, this is not quite true. In Common Lisp (the dialect being used here), macros can break in rare, subtle ways. The obvious happens when you don't use GENSYM:
(defmacro foo (var)
`(let* ((x 3))
(setf ,var x)))
CL-USER> (let ((x 7))
(foo x)
x)
7
CL-USER> (let ((y 7))
(foo y)
y)
3
but is fairly easy to avoid. In practice, as long as you make judicious use of GENSYM and packages, the only thing you really need to worry about is someone FLETing a symbol in the package COMMON-LISP (or maybe an external symbol in your package):
(defmacro bar (x y z)
`(list ,x ,y ,z))
CL-USER> (let ((list '(7)))
(bar 1 2 3))
(1 2 3)
CL-USER> (flet ((list (a b c)
(+ a b c)))
(bar 1 2 3))
6
SBCL actually yelled at me when I tried this for violating the package lock on COMMON-LISP. So in practice, it's really not much of an issue, and is certainly a hell of a lot better than C macros.
If you're interested in learning more about this, look at Scheme's hygienic macros.
- nabla9 12y agoVariable capture in CL can be avoided with few helpful macros. (defmacro WITH-GENSYMS (syms &body body) `(let ,(loop for s in syms collect `(,s (gensym))) ,@body)) (defmacro WITH-UNIQUE-NAMES (vars &body body) `(let ,(loop for var in vars collect `(,var (make-symbol ,(symbol-name var)))) ,@body)) (defmacro REBINDING (vars &body body) (loop for var in vars for name = (make-symbol (symbol-name var)) collect `(,name ,var) into renames collect ``(,,var ,,name) into temps finally (return `(let ,renames (with-unique-names ,vars `(let (,,@temps) ,,@body)))))) (defmacro ONCE-ONLY ((&rest names) &body body) (let ((gensyms (loop for n in names collect (gensym)))) `(let (,@(loop for g in gensyms collect `(,g (gensym)))) `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n))) ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g))) ,@body))))) edit: added once-only
- fabriceleal 12y agoLet over Lambda uses this (maybe something like this?). Very cool book. http://letoverlambda.com/ http://letoverlambda.com/
- ICWiener 12y agoDon't forget ONCE-ONLY
- gus_massa 12y agoAnother place where macros in Lisp fail is that they may evaluate an expression twice. (Using the Scheme dialect, because I don't write good enough CL.) (define (inc! x) (set! x (+ x 1)) x) (define-syntax-rule (square x) (* x x)) (define n 5) (square (inc! n)) ;==> 42 This can be solved using a temporary variable as in the original article, (define-syntax-rule (skuare x) (let ([temp x]) (* temp temp))) (define m 5) (skuare (inc! m)) ;==> 42 But it's easier because you don't have to declare the type of temp. Also Scheme has hygienic macros, so you don't need to use gensym to avoid the variable capture of temp.
- sjolsen 12y ago> Another place where macros in Lisp fail is that they may evaluate an expression twice Strictly speaking, macros don't evaluate their expressions at all. Square is a bad example, though. Macros are not for writing function-like expressions; they're for extending (or more generally transforming) the syntax of the language. Inline functions and compiler macros are more appropriate tools for things like square.
- gus_massa 12y ago> Strictly speaking, macros don't evaluate their expressions at all. OK, you are right. A better example is a bad implementation of a short circuiting or: (define-syntax-rule (bad-or x y) (if x x y)) It can't be a function because sometimes you don't want to evaluate y, but in the resulting code x is sometimes executed twice.
- aidenn0 12y agoIt's actually illegal to rebind symbols in the package common-lisp: http://www.lispworks.com/documentation/HyperSpec/Body/11_abab.htm http://www.lispworks.com/documentation/HyperSpec/Body/11_aba... [edit] And as a note, one ought-not rebind symbols in other packages without taking into account macros from that package. This is the Common Lisp solution to the hygiene problem: don't bind symbols from other people's packages unless you want to change the behavior of other people's code. Any macro that only relies on the bindings of unexported symbols (and symbols from the CL package) is hygienic by default to users from other packages.
- musername 12y ago> there is no syntax that doesn't seem very true, either. From looking at it, I have no idea what the comma in ',var' means or what '((x 7))' is supposed to mean.
- sjolsen 12y ago> that doesn't seem very true, either Strictly, it isn't. The point isn't that there's no syntax, though; it's that there's very little syntax, compared to other languages (consider C++'s… what, 17 operator precedence levels? And the Turing-complete parser?). The comma in ',var' and '((x 7))' are examples of Common Lisp's built-in syntax. The former is part of a shorthand notation[1] for injecting literal code into templates. The latter is part of the syntax of LET[2]. [1] http://www.lispworks.com/documentation/HyperSpec/Body/02_df.htm http://www.lispworks.com/documentation/HyperSpec/Body/02_df.... [2] http://www.lispworks.com/documentation/HyperSpec/Body/03_ababa.htm http://www.lispworks.com/documentation/HyperSpec/Body/03_aba...