Skip to main content

The Craft of Emacs

Emacs Lisp development

1 hour

Hopefully it wasn’t too difficult to set up your view. You probably called the sequence:

We’ll now compose these into a new M‑x xx‑view call.

The sequence above is a set of instructions. It’s a program.

Congratulations! You are now a programmer. Of course you are — you figured out what the sequence was. That’s what programmers do.

You also followed the instructions. In other words, you executed the program. This makes you, somewhat unconventionally, a computer.

Next, we’re going to get Emacs to execute the program. We’ll write the program up as a function. And for that, we’ll rely on the Emacs lisp Interpreter.

The interpreter is the Emacs component that executes functions.

A function is an Emacs program. It’s a set of instructions with a name. The name and instruction set are stored in the interpreter’s environment.

When given a function name, the interpreter searches its environment for the corresponding instruction set and executes it.

Unknowingly, we’ve already used it. Whenever we called a function — whether from the minibuffer, or via a keybinding — the interpreter executed it.

So far, we’ve called functions interactively: from the minibuffer or through keybindings. These methods are effective when editing with Emacs, but aren’t quite as useful when building functions.

When it comes to creating our own functions, we’ll write lots of experimental Elisp. It’s much faster to interpret the text for the code directly.

We need a buffer to experiment with Elisp. Emacs starts with one for that exact purpose — the *scratch* buffer.

Find the *scratch* buffer with M‑x list‑buffers.

Click on *scratch* to use to it.

The interpreter can call functions by interpreting text. To ask it to do so, we:

Let’s call coe‑xx‑basket‑view as an example.

Type the text (coe‑xx‑basket‑view) into the *scratch* buffer.

(coe‑xx‑basket‑view)
*scratch*

Recall that the grey box representing the cursor is known as the point.

Place the point anywhere within the buffer and call M‑x eval‑defun.

(coe‑xx‑basket‑view)
*scratch*
M‑x eval-defun 

You’ll see your basket buffer, just as if you called M‑x coe‑xx‑basket‑view interactively.

You’ve evaluated your first piece of Elisp. More congratulations are deserved!

At least, they ought to be. There’s a small chance that you simply read past the snippet without bothering to open the *scratch* buffer. Perhaps you thought you’d save yourself the hassle of typing.

I strongly urge you against this. You’re much more likely to forget things, and the code further on will seem far more complex than it should.

With Emacs, you can only learn by doing. Pour over the code as much as you like, carefully scrutinize each word, but you’ll only truly understand it when you execute it yourself.

This book has many little snippets labeled *scratch*, meant to be evaluated in your *scratch* buffer. Try not to read past one until you’ve ran it through the interperter, especially if you’re not sure what it does.

If you can, come up with your own snippets. Each time you see a new technique, think up experiments to play with it. Predict what the interpreter will do, then evaluate the snippet and see if you were right. Running your own experiments is far more interesting than reading through these ones.

M‑x eval‑defun caused the interpreter to call a function from the text. It read the code around the point, found (coe‑xx‑basket‑view), and subsequently called the coe‑xx‑basket‑view function.

As a rule, the text for a function call is a function name within a pair of braces.

In our next experiments, we’ll test what happens when that rule is broken.

Non-existent functions

What if we call a function that doesn’t exist?

Ask the interpreter to save the rhino.

(save‑the‑rhino)
*scratch*
M‑x eval-defun 

Assuming your Emacs isn’t equipped for rhino conservation, you’ll get a rather obscure response.

Symbol’s function definition is void: save-the-rhino

The interpreter doesn’t have a save‑the‑rhino function in its environment. It is trying to convey this, but it does so rather strangely. “Symbol”, “definition” and “void” mean something in English, but their usual meanings aren’t related to finding functions.

In the realm of Emacs lisp, these are very precise terms. You don’t need to know them quite yet. It suffices to know that the interpreter didn’t understand something, and it probably related to functions.

Balancing braces

The interpreter determines what code to read by finding a matching brace. What if there isn’t one?

Evaluate coe‑xx‑basket‑view without a closing brace.

(coe-xx-basket-view  
*scratch*
M‑x eval-defun 

The interpreter’s response is a little clearer.

Scan error: "Unbalanced parentheses", 1, 20

There are “unbalanced parentheses”: the number of opening braces doesn’t equal the number of closing braces.

The two numbers, 1 and 20, refer to the line and column of text where the brace might be missing. So long as we remember what these numbers mean, we’ll know where to type the missing brace.

Its grammar could be better; this is a “scan error”, but you’re forgiven for thinking that you must somehow scan an error.

Remove the opening brace. Do you see the same error?

coe-xx-basket-view)  
*scratch*
M‑x eval-defun 

Unfortunately not.

Wrong type argument: listp, coe-xx-basket-view

You may be wondering why the interpreter is terrible at communicating.

Why doesn’t it print “the save‑the‑rhino function doesn’t exist”, or “there’s a missing brace around the word coe‑xx‑basket‑view”?

Error messages are designed to be read by humans after all, so why aren’t they more human-friendly?

There are a few reasons for this.

Firstly, the interpreter lacks information. When we purposefully left out a brace, the interpreter didn’t know whether a brace was missing or an extra one was added, and couldn’t guess how the code could be corrected.

Secondly, its errors are designed for experienced Emacsers.

You are writing your first function call in Elisp. But to the interpreter, you could be the most veteran Elisper in the world, knowing everything about its inner workings, and so it gives a precise report of what went wrong.

This is entirely unhelpful. You don’t know what a “symbol”, “argument” or “listp” is, nor should you be expected to, and so the interpreter confounds far more than it clarifies.

In being aware of what we don’t understand, we actually know more than the interpreter. We can choose not to focus on the details of its error, and simply glean that there’s a bug in our code.

Once we understand the interpreter’s workings, we’ll revisit these errors and explain them.

Someday, when you’re an Elisp veteran, you’ll be able to use tools to prevent these errors, or inspect them when they occur.

We could set up these tools now, but they aren’t as exciting as writing code. And besides, our code is short enough to fix with a keen eye.

We are now familiar enough with the interpreter to continue our task: writing a xx‑view function to set up the pattern view.

This involves adding a function to the interpreter’s environment.

The environment is analogous to a dictionary: a function name is a word and its instruction set is its meaning.

coe-xx-basket-view
function
instructions...
coe-xx-basket-add
function
instructions...
...

For this reason, an instruction set is known as a function definition and the act of creating a function is termed defining.

Through these terms, the earlier error makes a little more sense.

Symbol’s function definition is void: save-the-rhino

In programming speak, “void” means non-existent. save‑the‑rhino didn’t have an instruction set. Let’s add one to the environment.

Define save‑the‑rhino.

(defunsave‑the‑rhino
()
(coe‑xx‑basket‑view)
)
*scratch*

A function definition has several parts:

Evaluate the definition with M‑x eval‑defun.

This adds save‑the‑rhino to the environment.

save-the-rhino
function
(coe-xx-basket-view)
coe-xx-basket-view
function
instructions...
...

After doing so, you can successfully save‑the‑rhino.

(defunsave‑the‑rhino
()
(coe‑xx‑basket‑view)
)
(save‑the‑rhino)
*scratch*

A conservationist would rightly object. We didn’t save any rhinos!

The interpreter doesn’t care about this. Names are for humans to worry about. You can name a function whatever you wish, its body doesn’t have to do what the name implies.

Function names are sometimes quite misleading. eval‑defun, for instance, evaluates more than just definitions.

Most functions have helpful descriptions that are far more enlightening than their name. You can read these using describe‑function.

We can do a better job of naming here.

Rename save‑the‑rhino to xx‑view and re-evaluate.

(defunxx‑view
()
(coe‑xx‑basket‑view)
)
(xx‑view)
*scratch*

The text defining save‑the‑rhino no longer exists. Does this mean that save‑the‑rhino has been deleted from the environment?

Call save‑the‑rhino to test this.

(defunxx‑view
()
(coe‑xx‑basket‑view)
)
(xx‑view)
(save‑the‑rhino)
*scratch*

The environment contains both save‑the‑rhino and xx‑view.

xx-view
function
(coe-xx-basket-view)
save-the-rhino
function
(coe-xx-basket-view)
...

Let’s try modifying xx‑view to see what happens.

Add the remaining function calls to the body and re-evaluate.

(defunxx‑view
()
(normal‑mode)
(coe‑xx‑mode)
(coe‑xx‑ruler‑mode)
(coe‑xx‑basket‑clear)
(coe‑xx‑basket‑add)
(coe‑xx‑basket‑view)
)
(xx‑view)
*scratch*

Evaluating (xx‑view) now throws an error.

Search failed: "LEGEND"

This tells us two things.

Firstly, the environment contains the new definition of xx‑view. The previous body has been replaced.

xx-view
function
(normal-mode) (coe-xx-mode)...
...

Secondly, the buffer from which we call coe‑xx‑mode is important. The function should only be called from a buffer with the text "LEGEND". In fact, it should only be called from a cross-stitch buffer with a pattern to display. When we evaluated it using M‑x eval‑defun, we called it from the *scratch* buffer. There was no pattern or legend and the interpreter complained.

In order to use xx‑view, it must to be called from the minibuffer when viewing a cross-stitch pattern. To do this, we need to make a minor modification.

We can’t yet call our function in the minibuffer: M‑x xx‑view isn’t matched.

While all functions can be called from Elisp, only a few are exposed through the minibuffer. This makes sense. There are all sorts of functions that are useful within code, but that we don’t use when editing.

A function that is called from the editor is known as a command. Commands can be called from the minibuffer, bound to keys, and placed in the menu bar.

Promote xx‑view to a command by adding a call to interactive within it.

(defunxx‑view
()
(interactive)
(normal‑mode)
(coe‑xx‑mode)
(coe‑xx‑ruler‑mode)
(coe‑xx‑basket‑clear)
(coe‑xx‑basket‑add)
(coe‑xx‑basket‑view)
)
*scratch*

The (interactive) call is a signal to the interpreter. The interpreter reads it when the definition of xx‑view is evaluated and registers xx‑view as a command.

xx-view
functioncommand
(normal-mode) (coe-xx-mode)...
...

The (interactive) call does nothing when evaluated. We can place it anywhere within the body, but conventionally it’s placed at the top.

Call M‑x xx‑view in the logo.xx buffer.

Our xx‑view function is complete.

But wait — we haven’t saved any of our work. The *scratch* buffer is temporary. It will disappear we quit Emacs, along with the code it contains.

We want to save xx‑view in a file. We also want it to be loaded into the environment when we start Emacs. Otherwise, we’d need to find it and call M‑x eval‑defun whenever we restart.

We can tackle both of these by moving the code to a startup file. This differs depending on your distribution.

Your code should live in your initialization file, called init.el.

Open it with M‑x find‑file ~/.emacs.d/init.el.

The “.el” file extension stands for Elisp. The code in the initialization file is interpreted when Emacs starts up. All functions defined here are loaded into the environment.

Add xx‑view to the end of the file.

···
(defunxx‑view
()
(interactive)
···
(coe‑xx‑basket‑view)
)
init.el*

Notice the subtle asterisk after the name init.el*. Emacs is hinting that the buffer has unsaved changes.

The simplest place to put your code is in your Doom configuration file, called config.el.

Open it with M‑x find‑file ~/.doom.d/config.el.

The “.el” file extension stands for Elisp. The code in the configization file is interpreted when Emacs starts up. All functions defined here are loaded into the environment.

Add xx‑view to the end of the file.

···
(defunxx‑view
()
(interactive)
···
(coe‑xx‑basket‑view)
)
config.el*

Notice the subtle asterisk after the name config.el*. Emacs is hinting that the buffer has unsaved changes.

Save them with M‑x save‑buffer.

Let’s check that the function is really loaded on startup.

Quit and reopen Emacs

Open the logo.xx file and call M‑x xx‑view.

Reassured, you can now kick back and take a break.

Our own custom functions have the same status as Emacs’s own. xx‑view and normal‑mode are both functions in the interpreter’s environment.

We can redefine a function by loading a new definition. After all, we’ve already redefined xx‑view several times.

This begs the question: can we redefine Emacs’s own functions?

Redefine normal‑mode.

(defunnormal‑mode
()
(interactive)
(non‑existent)
)
*scratch*

We can redefine it without problems. What happens when we use it?

Call M‑x normal‑mode.

The error implies that the function was indeed replaced:

Symbol’s function definition is void: non-existent

For the Machiavellian

normal‑mode wasn’t crucial to Emacs — it was a mild, unobtrusive target when it came to function overwrites. We can be far more cunning.

We can overwrite the eval function, the workhorse of the interpreter.

This experiment could go badly wrong. But hey, you have a *scratch* buffer. Live dangerously.

Save any important files.

Redefine eval.

(defuneval
()
(muahaha)
)
*scratch*

Interestingly, we can load the definition. Let’s see what happens afterwards.

Try calling M‑x save‑buffer. How about M‑x list‑buffers? Or M‑x split‑window‑right?

Everything we call fails with wrong‑number‑of‑arguments … (muahaha).

We’ve successfully broken Emacs.

Rest assured, everything in *scratch* is temporary. Restart Emacs and all will be well

Restart Emacs.

In practice, we’ll never want to overwrite Emacs’s own functions. To ensure we don’t do so accidentally, we’ll pay close attention to our function names.

All the functions you write will be prefixed by xx. This ensures that they never clash with anything in Emacs.

There’s no end to experimentation. Here are a few more for you to try:

Add a non-existent function call to the body of a definition. Does the interpreter raise error when the function is defined, or when it’s evaluated? What does this tell you about the interpreter’s checks?

Try deleting the function definition’s argument list. How does the interpter react?

The interactive call was placed at the top of a function body. Does it have to be? Try placing it in the middle.