Search code examples
emacselisp-macro

Emacs Lisp Macro Does Not Expand Alist


Here's my macro, what it is supposed to do is to wrap a body in let with bindings from vars-alist

(defmacro with-vars-alist (vars-alist &rest body)
  `(let (,@(mapcar (lambda (cell) (list (car cell) (cdr cell))) vars-alist))
     ,@body))

When I am looking at what it expands to using following code

(defvar my-vars-alist '((var1 . "var1")
                        (var2 . "var2")))
(macroexpand-1 (with-vars-alist my-vars-alist `(concat ,var1 ,var2)))

I get an error cons: Wrong type argument: sequencep, my-vars-alist

However checking it (sequencep my-vars-alist) return t.

The error is probably has some simple solution, but I am just unable to find it.


Solution

  • Remember that arguments to macros are un-evaluated, which means that when you pass my-vars-alist as an argument, it is passed verbatim as the symbol my-vars-alist.

    Therefore during the macro expansion, vars-alist evaluates to the symbol my-vars-alist rather than the list ((var1 . "var1") (var2 . "var2")).

    So the error isn't complaining that the variable my-vars-alist doesn't contain a sequence as its value, but rather that the symbol my-vars-alist is not itself a sequence (which is correct -- it's a symbol).

    checking it (sequencep my-vars-alist) return t.

    Which is also correct, as there my-vars-alist is evaluated as a variable to its value of ((var1 . "var1") (var2 . "var2"))

    So you need to eval that argument. e.g.:

    ,@(mapcar (lambda (cell) (list (car cell) (cdr cell)))
              (eval vars-alist))
    

    As vars-alist is already being evaluated to the symbol my-vars-alist, this change means that we are passing that symbol my-vars-alist to eval, which evaluates it as a variable to obtain the list needed for mapcar.


    You probably also wanted to quote the form you pass to macroexpand-1 (or use M-x pp-macroexpand-last-sexp).