0

How can I conditionally remove a let-binding in a defun created in a lisp macro? Here, I want to remove the first let-bound variable from the resulting function, but am running into a problem where I have a nil that sticks around in the let bindings, causing an error.

For example, the resulting function uses let or let* and should remove the first variable assignment depending on arg. But the way I have it written the problem comes from replacing var1 with nil. I'm not sure how to get rid of the nil.

(cl-defmacro mac (cmd &optional arg)
  (let ((fn (intern (concat "fn-" cmd))))
    `(defun ,fn ()
       (interactive)
       (,(if arg 'let* 'let)
        (cons
         ,(when arg
            '(var1 1))
         (var2 (if (bound-and-true-p var1) (+ var1 1) 1)))
        (message "%s" var2)))))

(mac "fun")    ; Attempt to set constant: nil
(mac "fun2" t) ; no problem
;; (symbol-function 'fn-fun)
;; (symbol-function 'fn-fun2)
;; (fn-fun)
;; (fn-fun2)
Drew
  • 29,895
  • 7
  • 74
  • 104
Rorschach
  • 31,301
  • 5
  • 78
  • 129
  • 1
    The easiest workaround is probably to use `,@(when arg '((var1 1)))`. Is that `cons` supposed to be a function call? It's quoted, so right now you're also defining a variable named `cons`. It looks unnecessary to me. – jkiiski Jul 23 '16 at 22:38
  • `macroexpand` is your friend. Use it to find out just what your macro calls expand to. In this case it tells you that the relevant part expands to `(let (cons nil (var2 ...))...), which binds a variable named `cons` and tries to bind one named `nil`. – Drew Jul 23 '16 at 22:48
  • Your question is not a question about Emacs Lisp, per se. It's a question about Lisp macros. I added tag `lisp-macros`, because on StackOverflow the tag `macros` doesn't really mean the same thing. – Drew Jul 25 '16 at 01:53

1 Answers1

1

The easiest workaround is probably to use ,@(when arg '((var1 1))). ,@ is used to splice a list into the position. Since WHEN returns NIL if the condition fails, and NIL is the same as an empty list, the splice effectively ignores it. When the condition succeeds ((var1 1)) is returned, and (var1 1) is unwrapped by the splice.

jkiiski
  • 8,206
  • 2
  • 28
  • 44