Learn Emacs Lisp
================
Chr. Clemens Lahme
2024-10-29
Table of Contents
-----------------
- Introduction
- System Requirements
- Batch Mode
- Hello World
- Variables
- Basic Types
- Identifying the Type of a Value
- Boolean Values
- Forms
- Command Line Arguments
- Quote
- Single Quote Argument
- List
- Nested Quotes
- Last Example
- Eval
- Backquote
- First and Rest
- The function Function
- Code As Data
- Code as Recursive Lists in Elisp
- Code As Data Evaluated
- Code As String Data
- How it works
- Multiple expressions in one string
- Security note
- Rest Arguments
- Lisp Macros
- A Simple Macro
- How It Works
- Backquote in Macros
- Macro Parameters
- Function Instead Of Macro
- Macro-Like Implementation
- Real World Macro Usage
- Logging
- Timing
- Macro Mechanics and Trade-offs
- Why Lisp Macros are Unique
- Practical Use Cases
- Downsides and Best Practices
- Summary
- Testing
- Release History
1. Introduction
---------------
Emacs Lisp is the extension language of the Emacs text editor, and a dialect of
Lisp in its own right. This document introduces it through small,
self-contained examples run from the shell in batch mode - a quick way to
experiment and to learn Elisp without getting distracted by an editor and its
typical use cases.
2. System Requirements
----------------------
* Linux.
* GNU Emacs version 27.2.
* All source and text encodings are of US-ASCII 7-bit.
* Use of full special-form or macro names (function, quote, backquote) rather
than their #', ', ` shortcuts. However the shortcuts , and ,@ are used and
not unquote or unquote-splicing, as these long form names don't seem to work
in Emacs 27.2.
3. Batch Mode
-------------
Start a new .el file and create a function with 'defun'. Then invoke the .el
file from a shell like:
emacs -Q --batch --load=src/script.el --file=work.txt --eval='(my-function)'
The file to work on (in the editor) is optional.
4. Hello World
--------------
Here is the minimal classical code example:
cat > src/hello-world.el <<EOT
; Do not edit this file.
(defun hello-world ()
(princ "Hello, world!\n")
)
EOT
And to run this code here is the command line to execute this code:
emacs -Q --batch --load src/hello-world.el --eval='(hello-world)'
This example is already more than a minimal code example, as a function is
defined and then invoked from the command line, instead of just printing
directly inside the .el file and just loading the .el file without the '--eval'
parameter.
When code gets evaluated, the first symbol inside the '(' and ')' brackets gets
invoked as a function with the following parameters. If parameters are
expressions themselves, then they get evaluated before their values gets used
in the final function invokation.
5. Variables
------------
For example, use the 'setq' function:
cat > src/variables.el <<EOT
; Do not edit this file.
(setq var-hello-world "hello, world\n")
(princ var-hello-world)
EOT
6. Basic Types
--------------
cat > src/types.el <<EOT
; Do not edit this file.
(princ "This is an integer: ")
(princ 15)
(princ "\n")
(princ "This is a floating point number: ")
(princ 15.0)
(princ "\n")
EOT
The basic data types are:
1. Integer
2. Float
3. Character
4. String
5. Symbol
6. List
7. Cons cell (which is the building block of lists)
8. Array (including vectors and strings)
9. Hash-table
10. Function (including lambda expressions, compiled functions, etc.)
11. Macro
12. Primitive function (built-in functions)
13. Record (as of Emacs 25, a record is a first-class object)
14. Boolean (though in Elisp, 'nil' is false and any non-nil
value is true, so it's not a separate type but a use of symbols)
15. Circular reference (a special case)
6.1 Identifying the Type of a Value
'''''''''''''''''''''''''''''''''''
The built-in function for this is 'type-of'. It takes one argument and returns
a symbol naming the type:
(type-of 15) ; => integer
(type-of 15.0) ; => float
(type-of "hello") ; => string
(type-of 'hello) ; => symbol
(type-of '(1 2 3)) ; => cons
(type-of [1 2 3]) ; => vector
(type-of (make-hash-table)) ; => hash-table
(type-of #'car) ; => subr
(type-of (lambda () 1)) ; => lambda
(type-of nil) ; => symbol
(type-of t) ; => symbol
Two Details
1. Lists return 'cons', not 'list'. In Lisp, lists are not a separate type -
they are chains of cons cells ending in 'nil'. Use 'listp' if you
specifically want to check "is this a list (including 'nil')?" versus
'consp' which is true only for proper cons cells (i.e. non-empty lists).
3. 'type-of' does not distinguish subtypes of function. '#'car' is a primitive
('subr'), but a compiled byte-code function comes back as
'compiled-function'. If you want the more refined object-class names from
Common Lisp tradition, '(require 'cl-lib)' and use 'cl-type-of', which
returns names like 'integer', 'string', 'cons', 'symbol', 'vector',
'hash-table', 'function', etc.
The Other Style: Type Predicates
In day-to-day Elisp code, the idiomatic way to test types is the family of
'*-p' predicates. Each one returns 't' or 'nil':
(integerp 15) ; => t
(floatp 15) ; => nil (15.0 would be t)
(stringp "hi") ; => t
(symbolp 'hi) ; => t
(listp '(1 2)) ; => t
(consp '(1 2)) ; => t (false for '())
(vectorp [1 2]) ; => t
(hash-table-p (make-hash-table)) ; => t
(functionp #'car) ; => t
(null nil) ; => t
(zerop 0) ; => t
(string-equal "a" "a") ; => t
Predicates are the preferred form inside 'if', 'when', 'cond', and similar -
much more so than '(eq (type-of x) 'integer)', which works but is verbose.
6.2 Boolean Values
''''''''''''''''''
For true in Elisp the symbol t is used while to represent false value nil is
used.
nil and t are symbols. Elisp has no separate boolean type. 'nil' happens to
also be the empty list '(), and t is the canonical true value, but both are
symbols as far as 'type-of' is concerned. To detect "is this value used as a
boolean?", test with 'if' directly - only 'nil' is false, every other value is
true.
7. Forms
--------
A "form" is not a data type like the ones in the previous section. It's a
syntactic/conceptual term. The Emacs Lisp manual dedicates a whole chapter to
"Forms" because they are the things the Lisp evaluator knows how to evaluate.
In a nutshell:
A form is a piece of Lisp data that can be given to the evaluator.
Whether something is a form has nothing to do with the data type of
the object itself; it depends on what the evaluator does with it.
So when you type (+ 1 2) it is a function-call form, and the integer
'15' is also a form (it is a self-evaluating form), and the symbol 'foo'
is a form (it is a symbol form that asks for the value of the variable
'foo').
(+ 1 2) ; a function-call form -> evaluates to 3.
15 ; a self-evaluating form -> evaluates to 15.
foo ; a symbol form -> evaluates to the value bound to foo.
The reason the introduction said "the first symbol inside the parens gets
invoked as a function" is a simplification of one specific kind of form - a
function-call form. There are several other kinds, and the evaluator
distinguishes them by looking at the first element of the list (or, in the
case of atoms, by recognising that they are atoms).
The kinds of forms
When the read-eval-print loop (or 'eval', or the compiler) sees a form, it
classifies it into one of these groups:
1. Self-evaluating form - numbers, characters, strings, vectors. The
evaluator just returns the same object.
(eval 15) ; => 15
(eval "hello") ; => "hello"
(eval [1 2 3]) ; => [1 2 3]
2. Symbol form (variable reference) - a bare symbol that is not a
special form, macro, or function call. The evaluator looks up the
symbol's value as a variable.
(setq greeting "hi")
(eval 'greeting) ; => "hi"
Note: at the evaluator level 'greeting' is the form, and we have to
'quote' it so that the surrounding code does not itself try to look up
the variable before passing it to 'eval'.
3. Function-call form - a list whose first element is the name of a
function (or a lambda). All the other elements are evaluated
left-to-right, and the results are passed to the function.
(+ 1 2) ; function call, arguments evaluated
(princ "hi") ; function call
((lambda (x) (* x x)) 5) ; function call, lambda is the operator
4. Special form - a list whose first element is one of a small set of
built-in operators ('if', 'and', 'or', 'quote', 'let', 'setq',
'function', 'cond', 'prog1', 'catch', 'throw', ...). Special forms
do not follow the rule "evaluate all arguments first". Each one has
its own rules about which sub-forms are evaluated, when, and how
many times. That is exactly why things like 'if' and 'setq' exist -
you cannot write them as ordinary functions.
(if (> 3 2) "yes" "no") ; only the THEN branch is evaluated
(setq x 10) ; the symbol x is not evaluated
(quote (a b c)) ; the list is not evaluated
(function car) ; returns the function object, not called
5. Macro form - syntactically it looks like a function call
'(name arg1 arg2 ...)' but before the arguments are evaluated the
macro is expanded - the macro function returns a new form that
replaces the macro call, and that replacement is then evaluated.
'when', 'unless', 'dolist', 'incf' are macros.
(macroexpand '(when (> 3 2) (princ "yes") (princ "!\n")))
;; => (if (> 3 2) (progn (princ "yes") (princ "!\n")))
From the user's point of view, 'when' looks like a function call
but it is a different kind of form.
6. Quoted / backquoted forms - the special syntactic shortcuts
', #', backquote, ',', ',@' produce forms of their own. 'x is
short for (quote x), #'car is short for (function car), and
(backquote a ,b ,@c) is a backquote form. They are not a separate
kind at evaluation time - the reader turns them into lists whose
first element is 'quote' or 'function', or into a backquote
structure - but they are useful to know about while you are reading
code.
A single form can be many kinds at once
A list itself doesn't "know" what kind of form it is - that decision is
made by the evaluator based on what the first element is in the
current dynamic environment. The same list can even be a function call
form in one context and a macro form in another, depending on the
binding of the first symbol:
;; in fresh Emacs 'car' is bound as a function
(car '(1 2 3)) ; => 1 -- function-call form
;; but 'car' is also a perfectly good variable name
(let ((car 'something-else))
car) ; => something-else -- symbol form
So the type of a value ('type-of' returns 'cons' for a list) and the
kind of form the list represents are two different questions.
What "evaluating" actually does
If you want to see the evaluator at work, the function 'eval' is the
entry point. It takes one argument (a form) and returns the result.
This is the same function the REPL calls on every line you type, and
that the byte-compiler calls when it runs your '.el' file:
(eval '(+ 1 2)) ; => 3
(eval '(if (> 3 2) 'yes 'no)) ; => yes
(eval 15) ; => 15
(eval 'x) ; => error: void variable x
The classification rule for a form is roughly:
* atom that is self-evaluating -> return it
* symbol -> look up variable, return value
* list whose car is a special form-> use the special form's own rules
* list whose car is a macro -> expand, then evaluate the result
* list whose car is a lambda -> build closure, apply to evaluated args
* list whose car is a function -> evaluate args, apply function
* anything else -> signal 'void-function' error
Forms vs. the introduction's simplified description
In the "Hello World" section the document said:
When code gets evaluated, the first symbol inside the '(' and ')'
brackets gets invoked as a function with the following parameters.
That is the everyday case for function-call forms, which are the ones
you write most often. "Forms" is just the word for "anything the
evaluator can chew on", and the special forms / macros / symbol forms /
self-evaluating forms are the other cases the rule has to make
exceptions for. Once you have read this section you can reread the
introduction's sentence and mentally replace "function" with "function
or special form or macro", and you have the full picture.
8. Command Line Arguments
-------------------------
In Emacs Lisp, command line arguments passed after '--' are stored in the
variable 'command-line-args-left'. Here's a minimal script:
cat > src/print-args.el <<EOT
;; Do not edit this example file, it gets automatically generated.
(defun print-args ()
"Print each command-line argument on a new line."
(while command-line-args-left
(princ (format "%s\n" (pop command-line-args-left)))
))
;; Automatically run when loaded in batch mode
(when noninteractive
(print-args))
EOT
It can be run with:
emacs -Q --batch --load src/print-args.el -- foo bar "baz qux"
Output:
'''
foo
bar
baz qux
'''
How it works:
- 'command-line-args-left' is a list of strings containing all arguments after
'--'.
- 'pop' removes and returns the first element, so we loop until the list is
empty.
- The 'when noninteractive' block ensures the function runs automatically in
batch mode, but you can also call it explicitly with '--eval='(print-args)''
as you've done before.
9. Quote
--------
The 'quote' function returns its argument without evaluating it. It is so
common that it has a shorthand: a single quote character ' in front of the
expression.
Use it when you want a symbol or a list as data, rather than as code that is to
be immediately executed.
Example:
cat > src/quote.el <<EOT
;; Do not edit this file.
(princ "A quoted list: ")
(princ (quote (1 2 3)))
(princ "\n")
(princ "A quoted symbol: ")
(princ (quote hello))
(princ "\n")
(princ "Same with ': ")
(princ '(1 2 3))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/quote.el
Output:
'''
A quoted list: (1 2 3)
A quoted symbol: hello
Same with ': (1 2 3)
'''
Without 'quote', '(1 2 3)' would try to call '1' as a function and fail. With
'quote', it is just the list '(1 2 3)'.
9.1 Single Quote Argument
'''''''''''''''''''''''''
'quote' is a special form that takes exactly one argument and returns that
argument unevaluated. You cannot hand it two separate lists.
If you want to work with two quoted lists, you have two common options:
1. Quote each list individually
(quote (1 2 3)) ; => (1 2 3)
(quote (4 5 6)) ; => (4 5 6)
You can then use them separately, e.g., pass them to a function one at a
time.
2. Quote a single list that contains both lists
(quote ((1 2 3) (4 5 6))) ; => ((1 2 3) (4 5 6))
This gives you one list whose elements are the two inner lists.
Trying to write '(quote (1 2 3) (4 5 6))' will fail because 'quote' only
accepts one argument.
10. List
--------
'quote' and 'list' both produce lists, but they differ in one crucial way:
whether the elements inside are evaluated.
- 'quote' (or ') returns its argument exactly as written, without evaluating
anything.
- 'list' is a normal function: it evaluates each argument first, then builds a
list from the resulting values.
Here is a small example you can run:
cat > src/list-vs-quote.el <<EOT
; Do not edit this file.
(setq x 10)
(setq y 20)
(princ "quote: ")
(princ (quote (x y (+ x y))))
(princ "\n")
(princ "list: ")
(princ (list x y (+ x y)))
(princ "\n")
EOT
emacs -Q --batch --load src/list-vs-quote.el
Output:
'''
quote: (x y (+ x y))
list: (10 20 30)
'''
What happened?
- (quote (x y (+ x y))) is literal data: the symbols 'x' and 'y' and the unevaluated
expression '(+ x y)'.
- (list x y (+ x y)) first evaluates 'x' -> '10', 'y' -> '20', and '(+ x y)' ->
'30', then builds the list '(10 20 30)'.
When to use which
Use 'quote' when you want a fixed piece of data written directly in the code:
(quote (1 2 3)) ; a constant list
(quote (foo bar baz)) ; a list of symbols
(quote ("red" "green")) ; a constant list of strings
Use 'list' when the values should be computed at runtime:
(list (+ 1 2) (* 3 4)) ; => (3 12)
(list (current-buffer) (buffer-name))
A common mistake
Because 'list' evaluates its arguments, this will signal an error if 'foo' is
not defined:
(list foo bar)
If you meant the symbols 'foo' and 'bar', write:
(quote (foo bar))
;; or
(list 'foo 'bar)
The second form explicitly quotes each symbol, then 'list' evaluates "'foo" to
"foo".
Summary
'quote' is for literals, 'list' is for building values dynamically.
11. Nested Quotes
-----------------
When 'quote' is nested, the outer 'quote' protects everything inside from
evaluation. So an inner '(quote ...)' is not executed; it becomes ordinary
data: a list whose first element is the symbol 'quote'.
cat > src/nested-quote.el <<EOT
;; Do not edit.
;; Show the word "quote" in the output, not the apostrophe shorthand.
(setq print-quoted nil)
(princ "One quote inside another:\n")
(princ (quote (quote hello)))
(princ "\n")
(princ "Symbols quoted inside a quoted list:\n")
(princ (quote (name (quote age) (quote city) country)))
(princ "\n")
(princ "A quoted list containing another quoted list:\n")
(princ (quote ((quote one) (quote two) three)))
(princ "\n")
(princ "Evaluating a quoted quote gives the inner value:\n")
(princ (eval (quote (quote hello))))
(princ "\n")
EOT
Run it:
emacs -Q --batch --load src/nested-quote.el
Output:
'''
One quote inside another:
(quote hello)
Symbols quoted inside a quoted list:
(name (quote age) (quote city) country)
A quoted list containing another quoted list:
((quote one) (quote two) three)
Evaluating a quoted quote gives the inner value:
hello
'''
11.1 Last Example
'''''''''''''''''
The last example has two quotes, one inside the other, and then wraps the
whole thing in 'eval':
(eval (quote (quote hello)))
Let's step through it carefully.
Step 1: Evaluate the outer 'quote'
(quote (quote hello))
'quote' returns its argument without evaluating it. So the outer 'quote' returns exactly what is inside it:
(quote hello)
At this point it is just data: a list containing the symbol 'quote' and the symbol 'hello'.
If you printed this data, you would see:
(quote hello)
That is why the earlier lines in the example print '(quote hello)'.
Step 2: Pass that data to 'eval'
Now the code does:
(eval (quote hello))
'eval' takes a piece of Lisp data and treats it as code to run. So 'eval' sees:
(quote hello)
This is a normal 'quote' expression. It evaluates to:
hello
So the chain is
(eval (quote (quote hello)))
^------ Step 1 -----^
^--------- Step 2 ---------^
Step 1 returns: (quote hello).
Step 2 evaluates (quote hello) => hello.
Why it is not '(quote hello)'
'(quote hello)' is code that evaluates to 'hello'. 'eval' executes that code,
so you get the result of the evaluation ('hello'), not the unevaluated
expression ('quote hello').
If you wanted to keep '(quote hello)' as data, you would need another outer
'quote' to protect it from 'eval':
(quote (quote hello)) ; => (quote hello)
But because the example calls 'eval' on it, the inner 'quote' is actually
executed, and its value is 'hello'.
12. Eval
--------
The function 'eval' takes a Lisp object and evaluates it as Emacs Lisp code.
(eval '(+ 1 2)) ; => 3
In the nested-quote example, '(quote (quote hello))' returns the list '(quote
hello)'. Passing that list to 'eval' interprets it as code: 'quote' is called
with 'hello', so it returns 'hello'.
In general, 'eval' lets you turn data into executable code. It is useful for
metaprogramming, but it should be used sparingly - it can make programs harder
to follow and may run untrusted input.
13. Backquote
-------------
'backquote' is a macro that behaves similar to 'quote', but it allows you to
selectively evaluate parts of the list. Inside a 'backquote' expression you
use , to evaluate a single element, and ,@ to splice the elements of a list
into the surrounding list. There seems to be no 'unquote' long form in Emacs
27.2! So we stick to the short macro form of , and ,@.
Here is a small example you can run:
cat > src/backquote.el <<EOT
; Do not edit this file.
(setq name "Alice")
(setq age 30)
(setq hobbies (quote (reading hiking)))
(princ "A backquoted list with unquote:\n")
(princ (backquote (name ,name age ,age)))
(princ "\n")
(princ "A backquoted list with unquote-splicing:\n")
(princ (backquote (person ,name ,age hobbies ,@hobbies)))
(princ "\n")
(princ "Compare with quote:\n")
(princ (quote (name ,name age ,age)))
(princ "\n")
(princ "Compare with list:\n")
(princ (list (quote name) name (quote age) age))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/backquote.el
Output:
'''
Inserted CSS link into file: ./doc/index.html
A backquoted list with unquote:
(name Alice age 30)
A backquoted list with unquote-splicing:
(person Alice 30 hobbies reading hiking)
Compare with quote:
(name (, name) age (, age))
Compare with list:
(name Alice age 30)
'''
When to use which
Use 'quote' when you want a fixed, literal piece of data:
(quote (1 2 3)) ; a constant list
(quote (foo bar baz)) ; a list of symbols
Use 'list' when every element should be computed at runtime:
(list (+ 1 2) (* 3 4)) ; => (3 12)
Use 'backquote' when you want a mostly literal structure with a few computed
parts. This is especially common in macro definitions, where you build code
that has a fixed shape but contains a few variable pieces.
A common mistake
Because 'backquote' is a macro, it is expanded when the surrounding expression
is processed by the Lisp evaluator. In interpreted code this happens during
evaluation; in byte-compiled code it happens during compilation. The , and ,@
forms are only recognised inside a 'backquote'; outside of one they are
ordinary symbols and will cause strange behavior if evaluated.
14. First and Rest
------------------
We have car and cdr in Lisp, but they really suck as names. They are historical
accidents, as they come from IBM 704 assembler instructions. So let's add new
functions 'first' and 'rest' that do the same, kind of as an alias, and then we
can forget about the ancient baggage.
In Emacs Lisp the cleanest way to get readable 'first' and 'rest' names is to
use 'defalias'. 'defalias' is preferred over 'fset' because it plays nicely
with Emacs help commands like 'describe-function'.
Here is the code and an example how to use it:
cat > src/first-rest.el <<EOT
;; Do not edit this file.
;; Define friendly aliases for car and cdr
(defalias 'first (function car))
(defalias 'rest (function cdr))
(defun demo-first-rest ()
(let ((my-list '(alpha beta gamma delta)))
(princ (format "Original list: %s\n" my-list ))
(princ (format "First element: %s\n" (first my-list)))
(princ (format "Rest of list: %s\n" (rest my-list)))
)
)
(demo-first-rest)
EOT
Run it with:
emacs -Q --batch --load src/first-rest.el
Output:
'''
Original list: (alpha beta gamma delta)
First element: alpha
Rest of list: (beta gamma delta)
'''
15. The function Function
-------------------------
"#'" is the shorthand for "function".
"#'foo" = "(function foo)" - it returns the function definition of 'foo' as a value, without calling it.
Use it when you want to pass a function around, for example:
(defalias 'first #'car)
(mapcar #'length '("abc" "de" "f"))
(funcall #'+ 1 2)
It's the function-level counterpart to ' ('quote'): ' returns data; #' returns the callable function object.
16. Code As Data
----------------
Here a minimal program is put into a list and not evaluted but printed to
standard output:
cat > src/code.el <<EOT
; Do not edit this file.
(princ "A list of two lisp functions printed as code but not evaluated:\n")
(princ
(quote
(
(defun hello-world ()
(princ "Hello, world!\n")
)
(hello-world)
)
)
)
(princ "\n")
EOT
16.1 Code as Recursive Lists in Elisp
'''''''''''''''''''''''''''''''''''''
Code as recursive lists in Elisp is the core idea - with one important
clarification.
Every form is a list, and forms nest into lists, so the code is a tree of lists
- the building block of Lisp's metaprogramming power. The leaves of that tree
(the things inside the deepest lists) can be atoms of any type, not
lists. That's the small but precise correction.
The Principle: Homoiconicity
Lisp is called homoiconic because its code is written in the same data
structure the language manipulates: the list (specifically, chains of cons
cells). This is what makes "code is data" literal.
( (defun hello-world () (princ "Hello, world!\n" ) )
(hello-world )
)
This is a list of two lists, and the inner '(defun hello-world () ...)' is
itself a list containing another list '(princ "Hello, world!\n")'. So you can
descend arbitrarily deep - that's the "recursive" part.
The Caveat: Lists of Atoms
It's more accurate to say: Elisp code is a tree of nested lists, whose leaves
are atoms (symbols, numbers, strings, vectors, etc.).
Take a "simple" expression:
(princ "Hello, world!\n" )
- The outer '(...)' is a cons cell list.
- princ is a symbol (an atom).
- "Hello, world!\n" is a string (an atom).
So it's not the case that everything is a list - but every form (top-level
expression) is a list, and any sub-form within it is again a list, recursively,
until you reach atoms at the leaves.
Clarifications:
- Any form you can pass to 'eval' is a list or an atom being evaluated as a
value.
- Nested forms produce nested lists - a tree of cons cells.
- You can take any piece of code, quote it, and have a valid Lisp data
structure.
- An Elisp file is a sequence of top-level forms, not one giant list.
- Vectors, strings, hash tables inside code are not lists - they're other data
types living inside the list structure.
- Even a single symbol like 't' or '42' is a valid standalone form and isn't a
list.
Why It Matters
Because the code structure is a list structure, you can do things like:
;; Build code as data, then evaluate it
(let ((fn-name 'greet)
(who "Alice")
)
(eval (list fn-name who ) )
)
; => calls (greet "Alice")
Or your code can walk through the tree with 'car'/'cdr', generate macros, write
source-to-source transformers, etc. The 'backquote' / ',' / ',@' machinery you
saw earlier exists precisely because list-of-lists is the native shape of the
language.
16.2 Code As Data Evaluated
'''''''''''''''''''''''''''
Below is the extended version of the "Code As Data" example.
Instead of merely printing the quoted list, we now evaluate both expressions inside it.
The first expression defines the function 'hello-world'; the second calls it.
cat > src/eval-code.el <<EOT
;; Do not edit this file.
;; Extend code.el: evaluate the two expressions inside the quoted list.
;; Define friendly aliases for car and cdr
(defalias 'first (function car))
(defalias 'rest (function cdr))
(setq my-code (quote (
(defun hello-world ()
(princ "Hello, world!\n"))
(hello-world)
)))
(princ "Evaluating the two expressions inside the list:\n")
(eval (first my-code)) ;; Defines the function
(eval (first (rest my-code))) ;; Calls (hello-world)
(princ "Finished.\n")
EOT
Run it from the shell:
emacs -Q --batch --load src/eval-code.el
Output:
'''
Evaluating the two expressions inside the list:
Hello, world!
Finished.
'''
How it works
1. The variable 'my-code' holds a quoted list of two sub-lists:
'(defun hello-world ...)' and '(hello-world)'.
2. Because the whole list is quoted, it is just data – it is not executed at
load time.
3. '(first my-code)' returns the first element: '(defun hello-world ...)'.
Passing it to 'eval' executes that 'defun' form, so 'hello-world' becomes a
known function.
4. '(first (rest my-code))' returns the second element: '(hello-world)'.
When 'eval' runs that call, the just-defined function prints 'Hello,
world!'.
This shows how a complete Lisp program can be stored as quoted data and later
brought to life via 'eval'.
17. Code As String Data
-----------------------
Emacs Lisp can also treat code as a string. The standard approach is to read
the string into a Lisp object (a list, a symbol, a number, etc.) and then eval
that object. The function 'read' parses the first complete expression from a
string; 'read-from-string' does the same but also returns the remaining
unparsed text, which is useful when the string contains multiple expressions.
Here is a minimal example:
cat > src/string-code.el <<EOT
;; Do not edit this file.
;; A string containing a simple arithmetic expression
(setq code-string "(+ 1 2)")
(princ "String: ")
(princ code-string)
(princ "\n")
(princ "Evaluated: ")
(princ (eval (read code-string)))
(princ "\n")
;; A string containing a function definition and a call
(setq code-string2
"(progn (defun greet () (princ \"Hello from string!\n\")) (greet))"
)
(princ "Evaluating a string with a function definition and call:\n")
(eval (read code-string2))
(princ "Finished.\n")
EOT
Run it with:
emacs -Q --batch --load src/string-code.el
Output:
'''
String: (+ 1 2)
Evaluated: 3
Evaluating a string with a function definition and call:
Hello from string!
Finished.
'''
17.1 How it works
'''''''''''''''''
1. 'read' takes a string and parses the first complete Lisp expression it
finds. '(read "(+ 1 2)")' returns the list '(+ 1 2)' – not the result of the
addition, but the unevaluated code.
2. 'eval' then takes that list and evaluates it as Emacs Lisp code.
'(eval (read "(+ 1 2)"))' therefore yields '3'.
3. The second example uses a 'progn' to group two expressions: a 'defun' and a
call. 'read' parses the whole 'progn' form, and 'eval' executes it, defining
the function and immediately calling it.
17.2 Multiple expressions in one string
'''''''''''''''''''''''''''''''''''''''
If a string contains several expressions, 'read' only reads the first one. To
process all of them, use 'read-from-string' in a loop:
cat > src/string-multi.el <<EOT
;; Do not edit this file.
(setq code-string "(princ \"first\n\") (princ \"second\n\")")
(princ "Evaluating multiple expressions from a string:\n")
(let ((pos 0))
(while (< pos (length code-string))
(let ((result (read-from-string code-string pos)))
(eval (car result))
(setq pos (cdr result)))))
(princ "Finished.\n")
EOT
Run it with:
emacs -Q --batch --load src/string-multi.el
Output:
'''
Evaluating multiple expressions from a string:
first
second
Finished.
'''
'read-from-string' returns a cons cell: the car is the parsed object, the cdr
is the position in the string where parsing stopped. The loop advances 'pos'
until the whole string is consumed.
17.3 Security note
''''''''''''''''''
Evaluating strings as code is powerful but dangerous. Never 'eval' a string
that comes from an untrusted source, because it can execute arbitrary Emacs
Lisp. Use it only for trusted, locally generated code.
18. Rest Arguments
------------------
In Emacs Lisp, you can define a function that takes a variable number of
arguments by using the '&rest' keyword in the parameter list. When you use
'&rest', any arguments passed to the function after the fixed parameters are
collected into a single list. The symbol immediately following '&rest' becomes
the name of the variable that holds this list.
Here is an example:
cat > src/rest-args.el <<EOT
; Do not edit this file.
;; Define friendly aliases for car and cdr
(defalias 'first (function car))
(defalias 'rest (function cdr))
(defun sum-all (first-num &rest rest-nums )
"Sum the first argument with all remaining arguments."
(let ((total first-num))
(while rest-nums
(setq total (+ total (first rest-nums ) ) )
(setq rest-nums (rest rest-nums ) )
)
total
)
)
(princ "Sum of 1: ")
(princ (sum-all 1))
(princ "\n")
(princ "Sum of 1, 2, 3, 4: ")
(princ (sum-all 1 2 3 4))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/rest-args.el
Output:
'''
Sum of 1: 1
Sum of 1, 2, 3, 4: 10
'''
How it works:
1. The function 'sum-all' is defined with two parameters: 'first-num' and
'&rest rest-nums'.
2. 'first-num' captures the very first argument passed to the function.
3. '&rest' tells Emacs Lisp to gather all subsequent arguments into a
list. 'rest-nums' is the variable bound to that list.
4. When '(sum-all 1)' is called, 'first-num' is '1' and 'rest-nums' is 'nil'
(an empty list). The 'while' loop does not execute, and '1' is returned.
5. When '(sum-all 1 2 3 4)' is called, 'first-num' is '1' and 'rest-nums' is
the list '(2 3 4)'. The 'while' loop iterates over 'rest-nums', adding each
element to 'total' using our 'first' and 'rest' aliases, until the list is
empty.
This mechanism is extremely useful for writing flexible functions like
'format', 'message', or custom wrappers that need to accept and forward an
arbitrary number of arguments.
19. Lisp Macros
---------------
There are keyboard macros. They just repeat previously recorded key strokes. A
Lisp macro is something different.
An Emacs lisp macro form looks like a function call but is handled specially:
its arguments are not evaluated; instead, the macro transformer produces a new
form, and that new form is evaluated. It receives unevaluated code as
arguments, builds a new piece of code, and finally the transformer returns this
new code to the interpreter.
Macro arguments are not limited to atoms. A macro parameter is bound to the
unevaluated argument form, and that form can be an atom (a symbol, number,
string, ...) or a list that is itself a tree of nested code. Inside the macro
body you can treat that tree as ordinary Lisp data and manipulate it with
'car', 'cdr', 'cons', 'list', 'backquote', etc.
The interpreter then evaluates that returned code in place of the original
macro call. Because the macro sees the raw symbols and forms, it can create
new control structures or syntactic shortcuts that ordinary functions cannot.
'defmacro' defines a macro. Inside the macro body, 'backquote' (with ',' and
',@') is the natural tool for assembling the output code: you write a template
and punch holes where computed values should go.
19.1 A Simple Macro
'''''''''''''''''''
Here is a minimal macro that increments a variable by one. It uses 'backquote'
and "," ('unquote') to build a 'setq' expression.
cat > src/macros.el <<EOT
;; Do not edit this file.
;; A simple macro that increments a variable by 1.
(defmacro inc1 (var)
"Increment VAR by 1."
(backquote (setq ,var (+ ,var 1)))
)
;; Use it.
(setq counter 0)
(inc1 counter)
(princ (format "After one inc1: %d\n" counter))
(inc1 counter)
(princ (format "After two inc1: %d\n" counter))
;; Show the expansion without evaluating it.
(princ "Macro expansion of (inc1 counter):\n")
(princ (macroexpand (quote (inc1 counter))))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/macros.el
Output:
'''
After one inc1: 1
After two inc1: 2
Macro expansion of (inc1 counter ):
(setq counter (+ counter 1 ) )
'''
19.2 How It Works
'''''''''''''''''
1. Definition – '(defmacro inc1 (var) ...)' creates a macro named 'inc1'.
The parameter 'var' is not evaluated when the macro is called; it
receives the symbol that appears in the call (e.g., 'counter').
2. Expansion – The macro body uses 'backquote' to build a list:
(backquote (setq ,var (+ ,var 1 ) ) )
- The outer 'backquote' makes the whole expression a template.
- ',var' is replaced by the actual symbol passed in (here 'counter').
- The result is the list '(setq counter (+ counter 1))'.
3. Evaluation – The interpreter takes that returned list and evaluates it
as if you had typed it directly. So '(inc1 counter)' becomes
'(setq counter (+ counter 1))' and then runs.
4. 'macroexpand' – The function 'macroexpand' shows the code a macro
produces, without executing it. It is invaluable for understanding and
debugging macros.
19.3 Backquote in Macros
''''''''''''''''''''''''
'backquote' is what makes macros readable. Without it, the same macro would
look like this:
(defmacro inc1 (var)
(list (quote setq) var (list (quote +) var 1 ) )
)
With 'backquote' you write the target code almost literally, only marking the
parts that should be filled in:
(defmacro inc1 (var )
(backquote (setq ,var (+ ,var 1 ) ) )
)
When you need to splice a list of forms into the template (for example, a
macro body), use ',@' (unquote-splicing). A classic example is a custom
'when':
(defmacro my-when (condition &rest body )
(backquote (if ,condition (progn ,@body ) ) )
)
Here ',@body' inserts every form from 'body' into the 'progn', so
'(my-when (> x 0 ) (princ "positive" ) (setq seen t ) )' expands to
'(if (> x 0 ) (progn (princ "positive" ) (setq seen t ) ) )'.
Because the code is interpreted, macro expansion happens at load time (or
when the form is first evaluated). The expanded code is then evaluated
normally. This is exactly the mechanism that lets Lisp grow its own syntax.
19.4 Macro Parameters
'''''''''''''''''''''
A few precise points:
- Parameters are the names in the 'defmacro' parameter list.
- Arguments are the forms you pass when you call the macro.
- Macros do not evaluate their arguments first; they receive the arguments as
Lisp objects.
- If the argument is a list, it is a cons-cell tree. You can traverse it,
inspect it, or transform it at expansion time.
Here is a self-contained batch example.
cat > src/macro-args.el <<EOT
; Do not edit this file.
;; Count all atoms in an arbitrary form.
(defun count-atoms (form)
(cond ((null form) 0)
((atom form) 1)
((consp form) (+ (count-atoms (car form))
(count-atoms (cdr form))))))
;; Inspect the kind of form a macro receives.
(defmacro show-form (form)
(backquote
(progn
(princ (format "Macro argument: %s\n" (quote ,form)))
(princ (format " listp => %s\n" (listp (quote ,form))))
(princ (format " atomp => %s\n" (atom (quote ,form))))
(princ (format " atom count => %d\n" ,(count-atoms form))))))
;; Transform a function-call-shaped tree by reversing its arguments.
(defmacro reverse-call (call)
(backquote (,(car call) ,@(reverse (cdr call)))))
(princ "=== Atom argument ===\n")
(show-form hello)
(princ "\n=== List argument ===\n")
(show-form (+ 1 2))
(princ "\n=== Nested code tree argument ===\n")
(show-form (defun greet (name) (princ (format "Hello, %s!" name))))
(princ "\n=== Tree transformation at expansion time ===\n")
(princ "(reverse-call (- 10 3)) => ")
(princ (reverse-call (- 10 3)))
(princ "\n")
(princ "Expanded form: ")
(prin1 (macroexpand (quote (reverse-call (- 10 3)))))
(princ "\n")
EOT
Run it:
'''text
emacs -Q --batch --load src/macro-args.el
'''
Output:
'''text
=== Atom argument ===
Macro argument: hello
listp => nil
atomp => t
atom count => 1
=== List argument ===
Macro argument: (+ 1 2)
listp => t
atomp => nil
atom count => 3
=== Nested code tree argument ===
Macro argument: (defun greet (name) (princ (format Hello, %s! name)))
listp => t
atomp => nil
atom count => 7
=== Tree transformation at expansion time ===
(reverse-call (- 10 3)) => -7
Expanded form: (- 3 10)
'''
What the examples show:
1. 'show-form' receives its argument unevaluated.
- '(show-form hello)' gets the symbol 'hello'.
- '(show-form (+ 1 2))' gets the list '(+ 1 2)', not the number '3'.
- '(show-form (defun greet ...))' gets a whole nested code tree, not a defined function.
2. Inside the macro the argument is ordinary data.
'count-atoms' recursively walks the tree at macro-expansion time and the
macro inserts the resulting number into the generated code.
3. 'reverse-call' demonstrates an operation on the tree itself.
It takes '( - 10 3 )', uses 'car' and 'cdr' to rearrange it, and produces '(- 3 10)'.
So the answer to your question is: yes, a macro argument can be an entire tree
of code, and the macro can operate on that tree before returning the final form
to be evaluated.
20. Function Instead Of Macro
-----------------------------
Here is the code and explanation for creating a function (instead of a macro)
that increments a variable by 1.
While macros are powerful for creating new syntax, sometimes a regular function
is all you need. The challenge with a function that modifies a variable is that
functions evaluate their arguments. If you pass 'counter' to a function, it
evaluates to the value of 'counter' (e.g., '0'), not the symbol 'counter'.
To fix this, we pass the symbol explicitly quoted (e.g., ''counter'). Inside
the function, we use 'symbol-value' to get the current value, and 'set'
(instead of 'setq') to update the variable. 'set' evaluates both of its
arguments, so it expects a symbol as its first argument.
Here is the code:
cat > src/inc-function.el <<EOT
;; Do not edit this file.
;; Define a function that takes a symbol and increments its value by 1.
(defun inc (var-symbol)
"Increment the variable named VAR-SYMBOL by 1."
(set var-symbol (+ (symbol-value var-symbol) 1))
)
;; Use it.
(setq counter 0)
(inc 'counter)
(princ (format "After one inc: %d\n" counter))
(inc 'counter)
(princ (format "After two inc: %d\n" counter))
EOT
Run it with:
emacs -Q --batch --load src/inc-function.el
The expected output is:
'''
After one inc: 1
After two inc: 2
'''
How it works:
1. Definition – '(defun inc (var-symbol) ...)' creates a standard
function. Because it is a function and not a macro, its arguments are
evaluated before the function body runs.
2. Passing the Symbol – When calling '(inc 'counter)', the quote prevents
'counter' from being evaluated to '0'. Instead, the symbol 'counter' itself
is passed to the function.
3. Reading the Value – '(symbol-value var-symbol)' looks up the current value
of the variable named by the symbol (which is '0' on the first call).
4. Setting the Value – '(set var-symbol ...)' updates the variable. 'set' is
the evaluating counterpart to 'setq' (which stands for "set quoted"). It
takes the symbol 'counter' and assigns the new calculated value to it.
20.1 Macro-Like Implementation
''''''''''''''''''''''''''''''
But in order to demonstrate the principle of how macros work, let's reimplement
the function inc again in two separate steps, one create code to increase the
variable, and the last one to evaluate the code.
1. Build a piece of code whose shape is '(setq <var> (+ <var> 1))'.
2. Return that code as data.
3. Evaluate it explicitly with 'eval'.
Because it is a function, its argument is evaluated first, so you must still
pass the variable name as a quoted symbol: '(inc2 'counter)'.
cat > src/inc-via-eval.el <<EOT
;; Do not edit this file.
(defun inc2 (var-symbol)
"Increment the variable named VAR-SYMBOL by 1.
This function mimics a macro: it first generates code, then evaluates it."
(let ((code (backquote (setq ,var-symbol (+ ,var-symbol 1)))))
(eval code)))
(defun inc2-code (var-symbol)
"Return the code that inc2 would evaluate."
(backquote (setq ,var-symbol (+ ,var-symbol 1))))
;; Use it.
(setq counter 0)
(inc2 'counter)
(princ (format "After one inc2: %d\n" counter))
(inc2 'counter)
(princ (format "After two inc2: %d\n" counter))
(princ "Code produced for (inc2 'counter):\n")
(princ (inc2-code 'counter))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/inc-via-eval.el
Expected output:
'''
After one inc2: 1
After two inc2: 2
Code produced for (inc2 'counter):
(setq counter (+ counter 1))
'''
How it works
- '(inc2 'counter)' is a function call, so the argument is evaluated first.
''counter' evaluates to the symbol 'counter', not its value '0'.
- Inside 'inc2', 'var-symbol' is therefore the symbol 'counter'.
- '(backquote (setq ,var-symbol (+ ,var-symbol 1)))' builds the list:
(setq counter (+ counter 1))
This is exactly the same code a macro would produce.
- '(eval code)' then executes that list as Emacs Lisp code.
- 'setq' does not evaluate its first argument, so it treats 'counter' as a
variable name and updates it.
The list-based equivalent
If you prefer not to use 'backquote', you can build the same code with 'list':
(defun inc2 (var-symbol)
(let ((code (list (quote setq)
var-symbol
(list (quote +) var-symbol 1))))
(eval code)))
This produces and evaluates the very same '(setq counter (+ counter 1))'.
Why this is "like a macro"?
A macro does two things automatically:
1. It receives unevaluated arguments and builds code.
2. The interpreter then evaluates that code in place of the macro call.
Here we do the same two steps, but manually inside a function: first build the
code with 'backquote'/'list', then call 'eval' on it. The difference is that a
macro hides the 'eval' step from the caller, whereas here it is explicit.
21. Real World Macro Usage
--------------------------
Let's recap Lisp macros one more time. The hard part about Lisp macros is
usually that macros look like functions, but they are not.
A function takes values and returns a value:
(+ 1 2) ; returns 3
(my-func x y) ; evaluates x and y, then returns a result
A macro takes code and returns new code, which is then evaluated:
(my-macro x y) ; transforms the expression into something else,
; then that something else is run
In Lisp, code is written as lists, and lists are ordinary data. So a macro is
just a function that manipulates lists of code. That is the whole trick.
This is very different from C macros, which are just text substitution. A Lisp
macro works on the actual structure of the program. Because Lisp code is
already written as nested lists, those lists are essentially its "Abstract
Syntax Tree" (AST) - a macro manipulates code directly as data.
21.1 Logging
''''''''''''
A concrete, easy example: conditional logging
Imagine you want a logging function. You write:
(log-debug "Result is %d\n" (expensive-calculation))
If debugging is turned off, you do not want '(expensive-calculation)' to run,
because it might be slow or have side effects.
A function cannot do this, because all arguments are evaluated before the
function is called. So '(expensive-calculation)' would always run.
A macro can do this, because it controls which code is generated:
cat > src/log-debug.el <<EOT
;; Do not edit this file.
(defvar debug-enabled nil
"If non-nil, log-debug emits output.")
(defmacro log-debug (fmt &rest args)
"Only evaluate and print ARGS when debug-enabled is non-nil."
(backquote
(when debug-enabled
(princ (format ,fmt ,@args ) )
)
)
)
(defun expensive-calculation ()
(princ "[expensive-calculation ran]\n" )
42
)
(princ "With debugging OFF:\n")
(log-debug "Result is %d\n" (expensive-calculation ) )
(princ "With debugging ON:\n" )
(setq debug-enabled t )
(log-debug "Result is %d\n" (expensive-calculation ) )
(princ "Lisp macro expands to:\n" )
(princ (format "%S\n"
(macroexpand (quote (log-debug "Result is %d\n"
(expensive-calculation )
)
)
)
)
)
EOT
Run it with:
emacs -Q --batch --load src/log-debug.el
The expected output is:
'''
With debugging OFF:
With debugging ON:
[expensive-calculation ran]Result is 42
Lisp macro expands to:
(when debug-enabled (princ (format "Result is %d
" (expensive-calculation))))
'''
Notice that when 'debug-enabled' is 'nil', 'expensive-calculation' is not
evaluated, so you see no "[expensive-calculation ran]".
21.2 Timing
'''''''''''
Another simple example is timing an expression. This wraps an expression (aka
any Lisp code) in timing code. Again, you could not do this as cleanly with a
function, because a function would evaluate its argument before it could
measure the time (You would do this by storing the start time and later
calculating and printing the difference in a second step).
cat > src/time-it.el <<EOT
;; Do not edit this file.
(defmacro time-it (form)
"Evaluate FORM and print how long it took."
(let ((start (make-symbol "start")))
(backquote
(let ((,start (current-time)))
(prog1 ,form
(princ (format "Elapsed: %f sec\n"
(float-time
(time-subtract (current-time) ,start)))))))))
(setq print-gensym t)
(princ "Timing (sleep-for 1):\n")
(princ (format "Return value: %s\n" (time-it (sleep-for 1))))
(princ "Macro expansion:\n")
(princ (format "%S\n"
(macroexpand
(quote (time-it (sleep-for 1))))))
EOT
Run it with:
emacs -Q --batch --load src/time-it.el
Expected output (the exact elapsed time will be slightly more than 1 second):
'''
Timing (sleep-for 1):
Elapsed: 1.001234 sec
Return value: nil
Macro expansion:
(let ((#:start (current-time))) (prog1 (sleep-for 1) (princ (format "Elapsed: %f sec
" (float-time (time-subtract (current-time) #:start))))))
'''
BTW, the '#:start' symbol is an uninterned symbol created by 'make-symbol', so it
cannot clash with any variable in the code being timed.
22. Macro Mechanics and Trade-offs
----------------------------------
The key to understanding and debugging Lisp macros is the function
'macroexpand'. It takes a macro call as data and returns the exact code the
macro produces. If a macro behaves unexpectedly, expanding it reveals what the
interpreter will actually evaluate.
Here is a small example you can run:
cat > src/macroexpand-demo.el <<EOT
;; Do not edit this file.
(defvar debug-mode nil "If non-nil, my-log emits output.")
(defmacro my-log (msg)
"Log MSG only when debug-mode is non-nil."
(backquote (when debug-mode (message ,msg))))
(princ "Macro expansion of (my-log (format \\"File: %s\\" (buffer-name))):\n")
(princ (macroexpand (quote (my-log (format "File: %s" (buffer-name))))))
(princ "\n")
EOT
Run it with:
emacs -Q --batch --load src/macroexpand-demo.el
Output:
'''
Macro expansion of (my-log (format "File: %s" (buffer-name))):
(if debug-mode (message (format "File: %s" (buffer-name))))
'''
Notice that the 'when' form inside the macro is internally translated to an
'if' by the Lisp evaluator. This demonstrates that a macro is essentially a
code generation function: it takes code as input and returns new code as
output.
22.1 Why Lisp Macros are Unique
'''''''''''''''''''''''''''''''
In languages like C, macros are handled by a preprocessor that performs simple
text substitution. A macro like '#define ADD(x,y) x+y' fails to handle grouped
expressions correctly because it lacks structural understanding.
In languages like Python or JavaScript, users cannot write macros at all; they
must wait for language designers to introduce new syntax (such as
'async/await').
Lisp is different because of its homoiconicity. Lisp code is structured exactly
like Lisp data-nested lists of symbols. Because '(+ 1 2)' is just a list
containing the symbol '+', the number '1', and the number '2', a Lisp macro is
simply a regular Lisp function that takes a list as input and returns a new
list as output. This gives the programmer the full power of the Lisp language
to rewrite code.
22.2 Practical Use Cases
''''''''''''''''''''''''
Macros are typically used for three main purposes:
1. Eliminating Boilerplate: If multiple functions all start with
'(interactive)', check if the buffer is read-only, and then do something,
a macro '(definteractive my-func ...)' can generate all this boilerplate
automatically.
2. Creating Domain Specific Languages (DSLs): The 'cl-loop' macro in Emacs is a
complete sub-language built inside Elisp. It transforms expressions like
'(loop for i from 1 to 10 collect i)' into standard 'while' loops.
3. Extending Language Syntax: Emacs Lisp's 'setq' natively sets one variable. A
macro allows extending this to '(setq a 1 b 2)', rewriting it into '(progn
(setq a 1) (setq b 2))'.
22.3 Downsides and Best Practices
'''''''''''''''''''''''''''''''''
While powerful, macros are often overused and come with significant downsides:
1. Tooling Friction: Macros introduce new syntax. Default indentation, linting
tools, and autocompletion may not understand custom macros, making the code
harder to read and maintain.
2. Debugging Complexity: If an error occurs inside macro-expanded code, stack
traces point to the expanded code, not the original macro
definition. Developers must mentally un-expand the code to find the issue.
3. The Golden Rule of Macros: "Do not write a macro if a function will do."
Functions are first-class citizens in Lisp; they can be passed as arguments,
mapped over, and composed. Macros cannot be easily manipulated this
way. Macros should be reserved for cases where you need to control
evaluation (like conditional logging) or bind variables (like 'let').
22.4 Summary
''''''''''''
Lisp macros are not magic. They are simply code that writes code at
expand-time. They allow you to abstract away repetitive patterns of syntax and
control flow, not just repetitive patterns of data like functions do.
Entire features of Emacs, such as 'use-package' or 'cl-defstruct', are
implemented as macros. However, the best Lisp programmers know when not to use
them, preferring functions whenever possible.
23. Testing
-----------
Do all example scripts run without error?
cat > bin/test_lisp.sh <<EOT
#! /bin/bash
set -e
cat doc/index.txt | grep -E '^emacs -Q ' > bin/test_lisp.tmp
source bin/test_lisp.tmp
echo "SUCCESS: $0 - $?."
EOT
24. Release History
-------------------
Version 1 - First released at http://techinvest.li/elisp/ on 2026-07-03.