1

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.

Drew
  • 29,895
  • 7
  • 74
  • 104
EvgeniySharapov
  • 3,078
  • 3
  • 27
  • 38

2 Answers2

4

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).

phils
  • 71,335
  • 11
  • 153
  • 198
  • Thanks. It works. My thought was, that since `vars-alist` is prepended by `,@` it will be evaluated. I tried using `,` but it doesn't work complaining about `(void-function \,)` – EvgeniySharapov Jul 25 '18 at 13:22
  • Yes, `vars-alist` *is* evaluated within the `,@(...)` form -- it is evaluated to the symbol `my-vars-alist`. Therefore `(eval vars-alist)` is passing `my-vars-alist` to `eval` and obtaining the list value to pass to `mapcar`. Note that trying to use `,vars-alist` within that form can't work because that implies a second level of nested backquoting which doesn't exist. – phils Jul 25 '18 at 22:31
2

I looks like what you're trying to do is let-bind the keys of an alist to their values, so you can use the keys as variables in the let body. There's a built in macro for this in recent Emacs:

(let-alist '((a . 1) (b . 2))
  (message "a: %d, b: %d" .a .b))
jpkotta
  • 9,237
  • 3
  • 29
  • 34