
Comparative Macrology - luu
http://www.wilfred.me.uk/blog/2014/09/15/comparative-macrology/
======
lispm
About the Common Lisp section:

Using _fresh_ symbols is not enough. GENSYM creates uninterned symbols - those
are not interned in a package. Thus referencing them in user code is not
possible.

Shadowing 'SETF' in Common Lisp would be non-conforming. SBCL for example
detects it and gives an error. The ANSI CL Standard, Section 11.1.2.1.2.
[http://www.lispworks.com/documentation/HyperSpec/Body/11_aba...](http://www.lispworks.com/documentation/HyperSpec/Body/11_abab.htm)
Still for user code in macro expansions it can be a problem.

If the SWAP macro really should work with places in a semantic compatible way,
then it needs to do more. Compare with the built-in ROTATEF macro. It makes
sure that place subforms are only evaluated once.

    
    
        ? (let ((a (vector 1 2)))
            (swap (aref a (print 0)) (aref a (print 1)))
            a)
    
        0 
        0 
        1 
        1 
        #(2 1)
    

Note that it prints 0 and 1 twice.

The built-in ROTATEF does it better: it prints them only once. This shows that
the subforms are only evaluated once.

    
    
        ? (let ((a (vector 1 2)))
            (rotatef (aref a (print 0)) (aref a (print 1)))
            a)
    
        0 
        1 
        #(2 1)
    
    

The EACH-IT macro has a bug: the body needs to be put into a PROGN. Otherwise
the LOOP macro might think that parts of the body are LOOP clauses.

    
    
        (defmacro each-it (list &rest body)
          `(loop for it in ,list
                 do (progn ,@body)))
    

For the purposes as an example, DOTIMES would be more idiomatic, since it
provides the basic stuff like declarations and goto tags. It is also the
traditional iteration macro for lists.

An alternative to PROGN would be LOCALLY, which also allows declarations. Thus
one could declare IT to be of a certain type, for example.

------
eddyb
There is an interesting claim about Rust:

> However, it prevents you from writing generic macros that use any l-value –
> we can’t write swap!(x[0], x[1]) as we could in Common Lisp.

As long as `macro_rules!` has existed (I could be wrong about before 0.8
though), you can wrote a macro that takes any expression, and will error from
the assignment when the expression is not an lvalue:

    
    
        macro_rules! swap {
            ($x:expr, $y:expr) => ({
                let tmp = $x;
                $x = $y;
                $y = tmp;
            })
        }

------
tolmasky
The hygienic C macro is broken. It breaks if your variable happens to already
be named "tmp": swap(x, tmp). This would work however:

    
    
      #define SWAP(x, y) {        \
          typeof (x) x##y = x; \
          x = y;              \
          y = x##y;            \
      }
    

Also, you can be tricky and "co-recurse" in macros, which is explained here:
[https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-
tricks,...](https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-
tricks,-tips,-and-idioms)

Regarding each-it, I think its pretty doable in C. Right off the top of my
head, this seems to work:

    
    
       #define eachIt(myArray) typeof(myArray[0]) it; for (int i = 0; i < sizeof(myArray)/sizeof(myArray[0]) && (it = myArray[i], 1); ++i)
    

This can now be used just as in the example:

    
    
       struct test_ { int one; double two; };
       typedef test_ test;
    
       test array[17];
    
       eachIt(array) {
         it.one = 1;
         it.two = 0.2;
       }
    

However, I _believe_ (?) that it depends on the version of the C standard
whether this is hygienic or not. When I compile in GCC it certainly works fine
because the i is counted as internal to the for loops scope, but I'm not sure
that's always the case. There is also the separate question of whether "it"
should be hygienic or not.

------
BruceM
I posted an explanation of how Dylan's macro system would handle this
elsewhere (including
[http://www.reddit.com/r/programming/comments/2gesag/comparat...](http://www.reddit.com/r/programming/comments/2gesag/comparative_macrology/ckihk2b)).

Dylan was one of the original languages to have an infix-syntax with a Lisp /
Scheme style macro system. (It is fairly similar to Scheme's syntax rules, but
there's an implementation of something more powerful that is used within the
compiler but isn't currently available to users.)

The examples that he is demonstrating in the other languages would like this
in Dylan:

    
    
        define macro swap!
          { swap! (?place1:expression, ?place2:expression) }
          =>
          { let value = ?place1;
            ?place1 := ?place2;
            ?place2 := value; }
        end;
    

and:

    
    
        define macro each-it
          { each-it (?collection:expression)
              ?:body
            end }
          =>
          { for (?=it in ?collection)
             ?body
            end };
        end;
    

Dylan's macros are hygienic by default, and as can be seen in the ``each-it``
macro, ``?=it`` is a way to violate hygiene without much difficulty, when
needed.

My full post as linked above contains links to documentation and other minor
details.

------
huhtenberg
At the risk of stating obvious - _typeof_ is a gcc-ism, not a part of the C
standard.

~~~
unwind
Thanks, that saved me some keystrokes.

Here's the GCC manual page:
[https://gcc.gnu.org/onlinedocs/gcc/Typeof.html](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html),
note that you can go "Up" to the main Extensions section where you'll find the
sub-page for typeof.

Also, the OP does the usual "everything is a function in C" error, which is
_weird_ in a text about macros in programming languages. The typeof extension,
like sizeof, is _not_ a function, it's a unary prefix operator. But nobody
seems to care about that for (the standard) sizeof operator, so I guess I
shouldn't be surprised.

Also, of course the local temporary variable should be marked as const.

------
qewrffewqwfqew
Tcl (1990) doesn't directly have macros, but with a calling convention that
resembles fexprs it doesn't need them:

    
    
        proc swap {_a _b} {
            upvar 1 $_a a
            upvar 1 $_b b
            foreach a $b b $a {}
        }
    

Foreach is a builtin, but is trivially defined as:

    
    
        proc each {ls script} {
            for {set i 0} {$i<[llength $ls]} {incr i} {
                uplevel 1 $script
            }
        }
    

Perhaps more interesting:

    
    
        proc until {cond script} {
            tailcall while "!($cond)" $script
        }

------
kazinator
F-expressions are ancient (1960's era Lisp). newLisp dug them out from a Lisp
landfill of discarded cruft; they do not chronologically belong in 1991. Tcl
has them too, incidentally. That is to say, Tcl allows for user-defined
interpretive operators that are very much like fexprs. Bash's eval can almost
do something like this; an eval sees the surrounding scope. What's missing is
the sugar to hide the eval.

"Text based fexpr":

    
    
        gen_swap_code()
        {
           echo "local tmp=\$$1;"
           echo "$1=\$$2;"
           echo "$2=\$tmp;"
        }
    
        fun()
        {
           local a=3
           local b=4
           eval $(gen_swap_code a b)  # sugar needed here
           echo $a $b
        }
    
        $ fun
        4 3

~~~
jrapdx3
I've used Tcl and Scheme fairly extensively so it's interesting to compare
them. Scheme has highly developed macros, and many implementations have non-
hygienic macros along with syntax-rules. Most of the coders I know say it's
rare to use anything other than the hygienic system, which is pretty easy to
learn and quite powerful.

Tcl doesn't have comparable macros, though some writers say in Tcl coding, the
whole thing is like writing a bunch of macros anyway, but I'm not sure I buy
that.

I'm also not entirely clear what you meant by "user-defined interpretive
operators ...". Current Tcl versions provide many facilities for specifying
semantics of expressions (proc, ensembles, interpreters, lambdas, etc.) and
the ability to redefine most any operator or built-in function if one so
desires.

The philosophy of "it means what I say it means" can lead to strange
constructions, but that is a basis for describing Tcl as "macro-like" in its
abundant, highly flexible features. The many ways it reflects Lisp/Scheme-like
traits is truly part of its appeal.

------
sklogic
There is also a somewhat newish approach in addition to the venerable ones
mentioned: a Katahdin-style syntax extensions, built upon the natural PEG
extensibility.

