12

When should I use macro in my program or not to?

This question is inspired by an informative answer by @tarsius. I believe many have great experiences to share others. I hope this question become one of Great Subjective Questions and help Emacs programmers.

3 Answers 3

14

Rule of thumb

Use macros for syntax, but never for semantics.

It's fine to use a macro for some syntactic sugar, but it's a bad idea to put logic into a macro body regardless of whether it's in the macro itself or in the expansion. If you feel the need to do that, write a function instead, and a "companion macro" for the syntactic sugar. Simply speaking, if you've got a let in your macro you're looking for trouble.

Why?

Macros have an awful lot of drawbacks:

  • They expand at compilation time and thus must be written carefully to maintain binary compatibility across multiple versions. For instance, removing a function or variable used in the expansion of a macro will break the byte code of all dependent packages that were using this macro. In other words, if you change the implementation of a macro in an incompatible way, all dependent packages must be recompiled. And binary compatibility isn't always obvious…
  • You must take care to maintain an intuitive evaluation order. It's all too easy to write a macro that evaluates a form too often, or at the wrong time, or in the wrong place, etc…
  • You must watch out for binding semantics: Macros inherit their binding context from the expansion site, which means that you don't know a priori what lexical variables exist, and whether lexical binding is even available. A macro must work with both binding semantics.
  • Related to this, you must watch out when writing macros that create closures. Remember, you get the bindings from the expansion site, not from the lexical definition, so watch out for what you close over and how you create the closure (remember, you don't know whether lexical binding is active at the expansion site and whether you even have closures available).
1
  • 1
    The notes on lexical binding vs macro expansion are particularly interesting; thanks lunaryorn. (I've never enabled lexical-binding for any code I've written, so it's not a conflict that I'd ever thought about.)
    – phils
    Mar 17, 2016 at 21:11
7

From my experience reading, debugging and modifying my and others' Elisp code I'd suggest to avoid macros as much as possible.

The obvious thing about macros is that they don't evaluate their arguments. Which means that if you have a complex expression wrapped in a macro, you have no idea if it will be evaluated or not, and in which context, unless you look up the macro definition. This might be not a big deal for yourself, since you know the definition of your own macro (currently, probably not a few years later).

This disadvantage doesn't apply to functions: all args are evaluated before the function call. Which means you can build up the eval complexity in an unknown debugging situation. You can even straight up skip the definitions of the functions unknown to you, as long as they return straightforward data, and you assume that they don't touch the global state.

To reword it in another way, macros become the part of the language. If you're reading a code with unknown macros, you're almost reading a code in a language that you don't know.

Exceptions to the above reservations:

The standard macros like push, pop, dolist do really a lot for you, and are known to other programmers. They're basically part of the language spec. But it's practically impossible to standardize your own macro. Not only it's hard to come up with something simple and useful like the above examples, but even harder is to convince every programmer to learn it.

Also, I don't mind macros like save-selected-window: they store and restore state and evaluate their args the same way progn does. Pretty straightforward, actually makes code simpler.

Another example of OK macros can be stuff like defhydra or use-package. These macros basically come with their own mini-language. And they still either treat simple data, or act like progn. Plus they're usually at top level, so there are no variables on the call stack for the macro arguments to depend on, making it a bit simpler.

An example of a bad macro, in my opinion, is magit-section-action and magit-section-case in Magit. The first one was already removed, but the second still remains.

5

I'll be blunt: I don't understand what "use for syntax, not for semantics" means. This is why part of this answer will deal with lunaryon's answer, and the other part will be my attempt at answering the original question.

When the word "semantics" used in programming, it refers to the way the author of the language chose to interpret their language. This typically boils down to a set of formulas which transform grammar rules of the language into some other rules the reader is assumed to be familiar with. For example, Plotkin in his book Structural Approach to Operational Semantics uses logical derivation language to explain what abstract syntax evaluates to (what does it mean to run a program). In other words, if syntax is what constitutes the programming language on some level, then it has to have some semantics, otherwise it's, well... not a programming language.

But, let's for the moment forget the formal definitions, after all, it's important to understand the intention. So, it seems like lunaryon would encourage his readers to use macros when no new meaning is to be added to the program, but just some sort of abbreviation. To me this sounds strange and in stark contrast to how macros are actually used. Below are examples which clearly create new meanings:

  1. defun and friends, which are an essential part of the language cannot be described in the same terms you would describe function calls.

  2. setf the rules describing how functions work are inadequate to describe the effects of an expression like (setf (aref x y) z).

  3. with-output-to-string also changes the semantics of the code inside the macro. Similarly, with-current-buffer and a bunch of other with- macros.

  4. dotimes and similar cannot be described in terms of functions.

  5. ignore-errors changes the semantics of the code it wraps.

  6. There's a whole bunch of macros defined in cl-lib package, eieio package and some other almost ubiquitous libraries used in Emacs Lisp which, while may look like function forms have different interpretations.

So, if anything, macros are the tool to introduce new semantics into a language. I cannot think of an alternative way of doing that (at least not in Emacs Lisp).


When I wouldn't use macros:

  1. When they don't introduce any new constructs, with new semantics (i.e. the function would do the job just as good).

  2. When this is just a sort of convenience (i.e. creating a macro named mvb which expands into cl-multiple-value-bind just to make it shorter).

  3. When I expect a lot of error handling code to be hidden by the macro (as has been noted, macros are difficult to debug).


When I would strongly prefer macros over functions:

  1. When domain-specific concepts are obscured by function calls. For example, if one needs to pass the same context argument to a bunch of functions that need to be called in succession, I would prefer to wrap them in a macro, where the context argument is hidden.

  2. When generic code in function form is overly verbose (bare iteration constructs in Emacs Lisp are too verbose to my taste and needlessly expose the programmer to the low-level implementation details).


Illustration

Below is the watered down version intended to give one an intuition as to what is meant by semantics in computer science.

Suppose you have an extremely simple programming language with just these syntax rules:

variable := 1 | 0
operation := +
expression := variable | (expression operation expression)

with this language we can construct "programs" like (1+0)+1 or 0 or ((0+0)) etc.

But as long as we didn't provide semantic rules, these "programs" are meaningless.

Suppose we now equip our language with the (semantic) rules:

0+0  0+1  1+0  1+1  (exp)
---, ---, ---, ---, -----
 0   1+0   1    0    exp

Now we can actually compute using this language. That is, we can take the syntactical representation, understand it abstractly (in other words, convert it into abstract syntax), then use semantic rules to manipulate this representation.

When we talk about Lisp family of languages, the basic semantic rules of function evaluation are roughly those of lambda-calculus:

(f x)  (lambda x y)   x
-----, ------------, ---
 fx        ^x.y       x

Quoting and macro-expansion mechanisms are also known as meta-programming tools, i.e. they allow one to talk about the program, instead of just programming. They achieve this by creating new semantics using the "meta layer". The example of such a simple macro is:

(a . b)
-------
(cons a b)

This is not actually a macro in Emacs Lisp, but it could have been. I chose only for simplicity sake. Notice that none of the semantic rules defined above would apply to this case since the closest candidate (f x) interprets f as a function, while a isn't necessarily a function.

4
  • 1
    "Syntactic sugar" isn't really a term in computer science. It's something used by engineers to mean various things. So I don't have a definitive answer. Since we are talking about semantics, then, the rule for interpreting the syntax of the form: (x y) is roughly "evaluate y, then call x with the result of previous evaluation". When you write (defun x y), y is not evaluated and neither is x. Whether you can write a code with equivalent effect using different semantics doesn't deny this piece of code having semantics of its own.
    – wvxvw
    Mar 17, 2016 at 21:35
  • 1
    Re' your second comment: Can you please refer me to the definition of macro you are using which says what you claim? I just gave you an example where new semantic rule is introduced by means of a macro. At least in the precise sense of CS. I don't think that arguing about definitions is productive, and I also think that definitions used in CS are the best match for this discussion.
    – wvxvw
    Mar 17, 2016 at 21:43
  • 1
    Well... you happen to have your own idea of what the word "semantics" means, which is kind of ironic, if you think about it :) But really, these things do have very precise definitions, you just, well... ignore those. The book I linked in the answer is a standard textbook on semantics as taught in CS corresponding course. If you just read the Wiki's corresponding article: en.wikipedia.org/wiki/Semantics_%28computer_science%29 you will see what it's actually about. The example is the rule for interpreting (defun x y) vs function application.
    – wvxvw
    Mar 17, 2016 at 22:05
  • Oh well, I do not think that this is my way of having a discussion. It was a mistake to even try and start one; I removed my comments for there's no point in all this. I'm sorry for having wasted your time.
    – user227
    Mar 17, 2016 at 22:30

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