coe-xx-basket-view
instructions...
Hopefully it wasn’t too difficult to set up your view. You probably called the sequence:
M‑x normal‑mode
M‑x coe‑xx‑mode
M‑x coe‑xx‑ruler‑mode
M‑x coe‑xx‑basket‑clear
M‑x coe‑xx‑basket‑add
M‑x coe‑xx‑basket‑view
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:
Write the text for the function call. This is the function name surrounded by braces.
Interpret the text with M‑x eval‑defun
.
The interpreter reads the text as a piece of Elisp code.
It evaluates the code. It discerns that the code is a function call, searches for the name in its environment, then executes its instructions.
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)
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)
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.
What if we call a function that doesn’t exist?
Ask the interpreter to save the rhino.
(save‑the‑rhino)
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.
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
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)
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
instructions...
coe-xx-basket-add
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))
A function definition has several parts:
defun
tells the interpreter we’re defining a
function.
save‑the‑rhino
is the function name.
()
is the argument
list. This is where the inputs, or arguments, to the function would
be named.
An argument is an input to a function — something we pass into it.
save‑the‑rhino
has no arguments, so this is left
empty.
(coe‑xx‑basket‑view)
is the body of the function.
The body is the “instruction set” that is executed when the function is evaluated.
Evaluate the definition with M‑x eval‑defun
.
This adds save‑the‑rhino
to the environment.
save-the-rhino
(coe-xx-basket-view)
coe-xx-basket-view
instructions...
...
After doing so, you can successfully save‑the‑rhino
.
(defunsave‑the‑rhino()(coe‑xx‑basket‑view))(save‑the‑rhino)
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)
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)
The environment contains both save‑the‑rhino
and xx‑view
.
xx-view
(coe-xx-basket-view)
save-the-rhino
(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)
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
(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))
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
(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))
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))
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))
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
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))
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.