macroslispcommon-lispmcl

Trouble with Lisp macros


I'm trying to write a macro in Lisp that re-implements let using itself. This is a trivial exercise which has no practical purpose; however after giving a response to a related question, I realized I should probably learn more about macros. They're touted as one of the great things about Lisp, but I rarely use them.

Anyway, here's what I tried first:

(defmacro mylet (args &rest exp) `(let ,args (dolist (x ,exp) x)))

but when I try something like:

 (mylet ((a 5) (b 2)) (print (+ a b)))

this throws up an error:

  #1=(PRINT (+ A B)) is not a symbol or lambda expression in the form (#1#) .

args (a and b) are set properly, but the print statement doesn't work. I think it's because I'm using two levels of indirection-- referring to a variable that I've created within the macro. But I can't seem to figure out how to fix it! Any ideas?


Solution

  • Your macro expands to:

    (LET ((A 5) (B 2))
      (DOLIST (X ((PRINT (+ A B)))) X))
    

    which is invalid because ((PRINT (+ A B))) is not a valid expression. There is also an issue that using an interned symbol in macro expansion can lead to variable capture, but that is not directly relevant (read more in PCL).

    Using DOLIST here is unnecessary, and compilcated to get right (you would have to convert all subforms to anonymous function in order to stick them in a list, funcall them in sequence and then store the final result in order to conform to PROGN behaviour). You can just use PROGN, or, since LET includes an implicit PROGN, just splice the body using the ,@ feature of backquote mechanism:

    (defmacro mylet (args &body exp) `(let ,args ,(cons 'progn exp)))
    
    (defmacro mylet (args &body exp) `(let ,args ,@exp))