
Why Lisp macros are cool, a Perl perspective (2005) - pmoriarty
http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html#
======
bmn_
Source filters are bad because you get a string and parse it yourself and then
get it wrong for edge cases. What if Perl already parsed a piece of source for
you and handed you a data structure instead? That fixes the greatest
shortcoming.

Solution 1: B::Deparse
<[http://yapceurope.lv/ye2011/talk/3597>](http://yapceurope.lv/ye2011/talk/3597>),
<[http://act.yapc.eu/gpw2014/talk/5266>](http://act.yapc.eu/gpw2014/talk/5266>)
Solution 2: B::CallChecker /
<[http://perldoc.perl.org/perlapi.html#cv_set_call_checker>](http://perldoc.perl.org/perlapi.html#cv_set_call_checker>)

In other words, you _do_ have access to the parser. It's not as neat as in
Lisp because of the lack of uniform syntax, but still allows for powerful
program transformations:
<[http://p3rl.org/Kavorka::Manual::Signatures>](http://p3rl.org/Kavorka::Manual::Signatures>)

------
sjolsen
> 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
Variable 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
Let over Lambda uses this (maybe something like this?). Very cool book.

[http://letoverlambda.com/](http://letoverlambda.com/)

------
pmoriarty
An easier to read ("=3D" converted back to "=") version of this post: [1]

Previous discussion of this post on HN: [2]

Also see: [3]

[1] -
[https://bpaste.net/show/a9bb9668beac](https://bpaste.net/show/a9bb9668beac)

[2] -
[https://news.ycombinator.com/item?id=795344](https://news.ycombinator.com/item?id=795344)

[3] - [http://stackoverflow.com/questions/267862/what-makes-lisp-
ma...](http://stackoverflow.com/questions/267862/what-makes-lisp-macros-so-
special/4621882)

------
rtpg
Very excellent argument against C macros in any case.

~~~
Elrac
Yep. Even as a long time C hacker who knows to avoid the common pitfalls, I
wasn't aware of just how many ways there are to botch up the use of macros.

There are lots of cases where macros look like the obvious and elegant
solution to a problem. Some of those cases are bugs waiting to happen.

------
jbert
Using setf is a nice example of something which a macro can do which a
function cannot (as well as being a good example of code-is-data).

Lots of posts discussing lisp macrology lack that kind of compelling use case.

The 'magic' used by setf is a 'destructuring bind':
[http://dunsmor.com/lisp/onlisp/onlisp_22.html](http://dunsmor.com/lisp/onlisp/onlisp_22.html)

------
agumonkey
reddit oldest (from 8ya) thread about it is interesting
[http://www.reddit.com/r/hackernews/duplicates/2o90ty/why_lis...](http://www.reddit.com/r/hackernews/duplicates/2o90ty/why_lisp_macros_are_cool_a_perl_perspective_2005/)

------
Ultimatt
Perl6 advent calendar post (from 2012) on macros might be of interest
[http://perl6advent.wordpress.com/2012/12/23/day-23-macros/](http://perl6advent.wordpress.com/2012/12/23/day-23-macros/)

------
GotAnyMegadeth
Could someone explain why all of the = signs have 3D after them?

$x =3D ...

~~~
jmartinpetersen
The link is to a web archive of a a mailing list. I'm guessing the software
doesn't properly handle the quoted-printable format of the e-mail.

In QP a = is used as the escape character, and =3D is the escape sequence for
=.

------
CyberDildonics
Does anyone know the basic differences between Julia's macros and Lisp macros?
Are they the same or is one more powerful/flexible than the other?

~~~
zaphar
Julias AST is _very_ similar to an S-Exp so the macros in that sense have a
lot of the same benefits of a lisp macro.

However they don't have the same ease since the AST does not necessarily match
the way the source looks. In Lisp the AST looks exactly like the source so
it's easier to visualize what a macro needs to do.

Julia requires you to mentally translate the source code syntax into an AST as
an extra step. That extra step doesn't make them any less powerful/flexible
though. It just makes them slightly harder to use.

~~~
grayclhn
Only loosely related, but I would love to see multiple dispatch come to
Julia's macros. I seem to spend a fair bit of each macro switching on the
expression's head, and would happily offload that step. (Of course, that might
be growing pains since I don't really have a good feel for writing macros in
the first place.)

Naturally, the best way to make that happen would be for me to write a macro
implementing it... :)

------
creatio
OT, I wish JS has macros. There is sweet.js, but I get the feeling its not
used very much....

------
areyousure
So how does one write the (square x) macro in Common Lisp? Presumably, one
would like to avoid evaluating the argument twice as well as accidental
variable capture.

------
lmm
Macros are powerful but still a bad idea. Like GOTO - using GOTO, you can
implement absolutely any kind of weird control flow structure you can imagine.
You have a lot more freedom than the usual if/else/for/while. And you can, if
you use it wisely, make code that's more coherent and consistent than any
other. But people don't.

Coherence of assignment is a very good idea. But you don't need macros to get
it. If you have a language with simple syntax where assignment is just another
function call (perhaps via a typeclass), everything in this example still
works; it's not the fact that lisp has macros for assignment that's important
here, it's the fact that assignment is _not special_ , not handled specially
in the parser; it's just another function. You do that, and you get the
advantages, with or without macros.

Lisp's lack of syntax makes macros easy, but they're still a mistake; they
still lead to unreadable code because you can't tell what a given piece of
code does until you understand which parts are macros and understand all of
them. Give users more structured ways of achieving the (often quite small)
number of things they want to do with macros. If they need more things, add
those things as first-class features, the same way e.g. exceptions were added
when people realized if/else/for/while didn't capture all the use cases for
goto. But don't throw up your hands and let every library rewrite code willy-
nilly.

~~~
davexunit
Sigh, the tired argument that because people may use a powerful feature
wrongly, that feature shouldn't exist.

>they still lead to unreadable code

Complete unsubstantiated nonsense.

>If they need more things, add those things as first-class features

If JavaScript had macros we could have had 'let' before ES6 instead of writing
'(function(x) { ... })(42)'. Do I really need to know that 'let' compiles to a
lambda with arguments applied? No, I just need to know that 'let' binds some
values to variables and evaluates the body. Do I need to know that 'cond'
compiles to a nested 'if' chain to know how to use it? No.

I don't understand why people think that syntax has to be behind a gate that
only the language implementors have access to. Syntactic abstraction makes
talking about problem domains easier, not unreadable.

~~~
hajile
Javascript has sweetJS. It isn't lisp, but it's about as close as most infix
languages can get (with the exception of Dylan).

The let in ES6 cannot be expressed in the same terms as a let in lisp. Mozilla
proposed that kind of let with the let block ('let (<assignments>) {
<statements> }').

The issue is that other idiots (in some quest to turn JS into Java or C#)
decided that a block scoping kluge was a better. After all, it makes
interoperability harder (old code using var), makes scoping rules implicit
(blocks without let are still bound to function), adds unnecessary cognitive
overhead (leading to more bugs as code gets harder to reason about and
visually parse) and generally adds a whole new set of ways for new and old JS
programmers to screw up.

