2

I want to improve my Elisp, but despite reading about the differences between functions and macros (including discussions here in Emacs SE and on Reddit), I still find examples where the distinction confuses me.

For example, in the "starter-pack" Radian, a macro can be found which wraps use-package ensuring some sort of partial application:

(defmacro use-feature (name &rest args)
  "Like `use-package', but with `straight-use-package-by-default' disabled."
  (declare (indent defun))
  `(use-package ,name
     :straight nil
     ,@args))

As a case study, would it not be possible to achieve the same with a defun? After all, this seems like a simple case of partial application, or am I missing something? Do we need a macro here just because use-package itself is a macro which we don't want a defun body to evaluate?

And would there be a safe way to improve this macro to take care of a possibly unwanted case (use-feature foo :straight t)?

Basil
  • 12,019
  • 43
  • 69
Daniel
  • 3,563
  • 16
  • 41
  • 1
    Macros can manage their raw arguments, while functions can't. If `use-package` is a functions, user will have to quote the arguments which is inconvenient and ugly, e.g., `(use-package 'foo :if '(<= 1 2) :commands #'foo-bar :bind '("C-c C-c" . foo))`, the same applies to `use-feature`, and if `use-feature` is not a macro, the use of it won't be like `use-package`. – xuchunyang Mar 25 '19 at 13:54
  • 4
    What @xuchunyang said, where "raw" args means they are not evaluated when passed to the macro code. A macro can do anything it likes with/to any arg it is passed, including ignore it or evaluate it. A macro produces *code*, which is then evaluated. A macro can do anything with its args, but it is *not* about partial application per se. Partial application is about function application to fewer than all of the function's args. If an arg to a macro is a function then it can apply it partially to some args. Otherwise, there is no connection between partial eval and macros. – Drew Mar 25 '19 at 14:05
  • 5
    As usual, a good way to learn about Elisp macros is to *ask Emacs*. `C-h i`, then choose `Emacs Lisp Intro`. In that intro-to-Elisp manual you'll find node [Lisp macro](https://www.gnu.org/software/emacs/manual/html_node/eintr/Lisp-macro.html), which will help. Or choose `Elisp`, the Emacs-Lisp reference and guide. There you'll find node [Macros](https://www.gnu.org/software/emacs/manual/html_node/elisp/Macros.html), which fills you in about Elisp macros. Much better than getting ad hoc answers here or on Reddit (IMHO). You're not the first person to learn Lisp macros - trust Emacs to help. – Drew Mar 25 '19 at 14:45
  • At least one of these comments deserves to be promoted to an actual answer. – Adam Spiers Aug 24 '20 at 16:56

1 Answers1

1

This is an old question, but every question deserves an answer. You basically had it with "because use-package itself is a macro which we don't want a defun body to evaluate". More specifically, it couldn't be a defun, because:

  1. Order Of Evaluation: use-package defines the order of evaluation (before and after loading its package) with, for example, the :init, :config and :after keywords. If use-feature were a defun, all arguments would be evaluated in order.

  2. Quoting: Some use-package sections, such as :bind take unquoted data as arguments (which would throw an error for not being a function), and many take an unquoted symbols (which emacs would try to evaluate as variables). You could require the user to manually quote everything, but that would make the use-feature API very different from use-package.

To answer the finall question at the end of the post - if there are 2 :straight keywords, the order of evaluation depends on the internals of use-package. If this version of the macro lets the user override it, swapping the lines ,@args and :straight t would probably change that behaviour. But might not, and you probably can't know without reading the code of use-package.