I have the following non-working elisp code:

(use-package eglot
         ;; For debug/testing, load local copy of eglot if it exists
         ,@(if (file-directory-p (concat user-emacs-directory "/eglot"))
               '(:load-path "eglot/")
             '(:quelpa ((eglot :fetcher github :repo "joaotavora/eglot"))))
         :commands eglot
         :hook ((vue-mode . eglot-ensure)
                (typescript-mode . eglot-ensure)
                (javascript-mode . eglot-ensure))
         (add-to-list 'company-backends  'company-capf))

You can see what I'm after -- if ~/.emacs.d/eglot exists, use the package from my local dir. Otherwise use quelpa to fetch it from its repo. I'm doing this because I'm working on some fixes. The idea is to use ,@ to interpolate items into the arg list.

But of course you can't use ,@ outside of a backquoted list. So I know I could pull all the args out and use (apply 'use-package eglot-args) but I'm wondering if there's a clever Elisp syntax to interpolate a list into an arg list dynamically like this.

  • 1
    I don't see any good way to do this. You can't (apply 'use-package eglot-args) because use-package is a macro (see apply docs). I would just put the conditional to either augment load-path or call quelpa within :init (or maybe :preface).
    – Dale
    Oct 30 '19 at 17:55
  • Maybe you can use the :if keyword of use-package to accomplish what you're after?
    – Tyler
    Oct 30 '19 at 18:04
  • @dsedivec, good point re: macros. Sigh. Adding stuff to :init would work, that's a good idea. @Tyler: :if would work but I'd have to duplicate the whole thing, prefer not to do that.
    – GaryO
    Oct 30 '19 at 18:29
  • 4
    Write your own Lisp macro that produces the use-package code you want. Pass it any args you want, and have it test anything you want.
    – Drew
    Oct 30 '19 at 20:04
  • Ha @Drew, clever! In any case, I ended up switching from package.el to straight.el which can just handle my case straight-up (pun intended).
    – GaryO
    Oct 30 '19 at 22:09

Since you have already a solution for your special use-case (switching from package.el to straight.el) that has nothing to do with macro-expansion I will answer your general question Is there a clean way to splice some args to an existing Lisp macro?.

Evaluation of a non-compiled macro form involves both macro expansion and evaluation of the resulting code. So you should just use eval on the backquoted macro form where you splice in the args you like as usual. Since the arg of eval is evaluated before the eval the parameters are spliced in before macro expansion of m.

I demonstrate that in the following with a simple macro m. For each top-level Elisp form the evaluation result is given in the comment following it.

(defmacro m (&rest arglist)
  `(quote (arglist: ,@arglist)))
;; m

(setq splice-in nil)
;; nil

(eval `(m ,@(when splice-in (list :myarg 1))))
;; (arglist:)

(setq splice-in t)
;; t

(eval `(m ,@(when splice-in (list :myarg 1))))
;; (arglist: :myarg 1)

If you use that construct in your init files it is no problem that the macro call is not byte-compiled since init files should not be byte-compiled anyway.

Citation of the Emacs Manual:

Byte-compiling your init file is not recommended (see Byte Compilation). It generally does not speed up startup very much, and often leads to problems when you forget to recompile the file.

Otherwise you can use the solution proposed by Drew in one of his comments. Maybe, he transforms it into an alternative answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.