LFE Style Guide
8 Meta-Language Guidelines
Macros bring syntactic abstraction, which is a wonderful thing. It helps make your code clearer, by describing your intent without getting bogged in implementation details (indeed abstracting those details away). It helps make your code more concise and more readable, by eliminating both redundancy and irrelevant details. But it comes at a cost to the reader, which is learning a new syntactic concept for each macro. And so it should not be abused.
The general conclusion is that there shouldn't be any recognizable design pattern in a good LFE program. The one and only pattern is: use the language, which includes defining and using syntactic abstractions.
Existing macros should be used whenever they make code clearer by conveying intent in a more concise way, which is often. When a macro is available in your project that expresses the concept you're using, you should not write the expansion rather than use the macro.
New macros should be defined as appropriate, which should be seldom, for common macros have already been provided by the language and its various libraries, and your program typically only needs few new ones relative to its size.
You should follow the OAOOM rule of thumb for deciding when to create a new abstraction, whether syntactic or not: if a particular pattern is used more than twice, it should probably be abstracted away. A more refined rule to decide when to use abstraction should take into account the benefit in term of number of uses and gain at each use, to the costs in term of having to get used to reading the code. For syntactic abstractions, costs and benefits to the reader is usually more important than costs and benefits to the writer, because good code is usually written once and read many times by many people (including the same programmer who has to maintain the program after having forgotten it). Yet the cost to the writer of the macro should also be taken into account; however, in doing so it should rather be compared to the cost of the programmer writing other code instead that may have higher benefits.
Using Lisp macros properly requires taste. Avoid writing complicated macros unless the benefit clearly outweighs the cost. It takes more effort for your fellow developers to learn your macro, so you should only use a macro if the gain in expressiveness is big enough to justify that cost. As usual, feel free to consult your colleagues if you're not sure, since without a lot of Lisp experience, it can be hard to make this judgment.
You should never use a macro where a function will do. That is, if the semantics of what you are writing conforms to the semantics of a function, then you should write it as a function rather than a macro.
When you write a macro-defining macro (a macro that generates macros), document and comment it particularly clearly, since these are harder to understand.
If your macro has a parameter that is a Lisp form that will be evaluated
when the expanded code is run, you should name the parameter with the suffix
-form. This convention helps make it clearer to the macro's user which
parameters are Lisp forms to be evaluated, and which are not. The common
end are exceptions to this rule.
You should follow the so-called
CALL-WITH style when it applies. This
style is explained at length
The general principle is that the macro is strictly limited to processing
the syntax, and as much of the semantics as possible is kept in normal
functions. Therefore, a macro
WITH-FOO is often limited to generating a
call to an auxiliary function
CALL-WITH-FOO with arguments deduced from
the macro arguments. Macro
body arguments are typically wrapped into a
lambda expression of which they become the body, which is passed as one
of the arguments of the auxiliary function.
The separation of syntactic and semantic concerns is a general principle of
style that applies beyond the case of
WITH- macros. Its advantages are
many. By keeping semantics outside the macro, the macro is made simpler,
easier to get right, and less subject to change, which makes it easier to
develop and maintain. The semantics is written in a simpler language — one
without staging — which also makes it easier to develop and maintain. It
becomes possible to debug and update the semantic function without having to
recompile all clients of the macro. The semantic function appears in the
stack trace which also helps debug client functions. The macro expansion is
made shorter and each expansion shares more code with other expansions,
which reduces memory pressure which in turn usually makes things faster. It
also makes sense to write the semantic functions first, and write the macros
last as syntactic sugar on top. You should use this style unless the macro
is used in tight loops where performance matters; and even then, see our
rules regarding optimization.
Any functions (closures) created by the macro should be named, which can be
When you write a macro with a body, such as a
WITH-xxx macro, even if
there aren't any parameters, you should leave space for them anyway. For
example, if you invent
WITH-LIGHTS-ON, do not make the call to it look
(defmacro with-lights-on (body b) ...). Instead, do
(defmacro with-lights-on (() body b) ...). That way, if parameters are
needed in the future, you can add them without necessarily having to change
all the uses of the macro.
Places where it is actually appropriate to use
EVAL are so few and far
between that you should consult with your reviewers; it's easily misused.
Often, what you really need is to write a macro, not to use
You may be tempted to use
EVAL as a shortcut to evaluating expressions
in a safe subset of the language. But it often requires more scrutiny to
properly check and sanitize all possible inputs to such use of EVAL than to
build a special-purpose evaluator. You must not use EVAL in this way at
Places where it is OK to use
- The implementation of an interactive development tool.
- The build infrastructure.
- Backdoors that are part of testing frameworks. (You should NOT have such backdoors in production code.)
- Macros that fold constants at compile-time.
- Macros that register definitions to meta-data structures; the registration form is sometimes evaluated at compile-time as well as included in the macro-expansion, so it is immediately available to other macros.
Note that in the latter case, if the macro isn't going to be used at the top- level, it might not be possible to make these definitions available as part of the expansion.