Skip to main content

The Craft of Emacs

Evaluation: S-expressions and special forms

1 hour

S-expressions are, loosely speaking, pieces of lisp code. There are several different kinds.

A string is an s-expression for text, denoted by double quotes "".

Strings can be evaluated by placing the point after the last quote and calling M‑x eval‑last‑sexp.

Evaluate each of these strings.

"xx""(this‑is‑not‑a‑function)"""
*scratch*
M‑x eval-defun 

A number is what you’d expect.

Check that these numbers evaluate to themselves.

421.107
*scratch*
M‑x eval-defun 

A group of parenthesized s-expressions is termed a list. We used them informally in the previous chapter.

Evaluate each list.

(length"Cookie")
(+411)
(tetris)
*scratch*
M‑x eval-defun 

The interpreter evaluates a list as function call. The first s-expression is interpreted as the function name and the rest as its arguments.

The result of evaluation can be passed as an argument to other functions by nesting the list within another. A nested list can be evaluated independently by calling the command M‑x eval‑last‑sexp with the point just after it.

Evaluate both the outer and inner lists.

(*
(+2010)
3)
*scratch*
M‑x eval-last-sexp 

The attentive reader will notice another kind of s-expression hidden among the rest: + and tetris are also s-expressions, known as symbols. A symbol is something that can be looked up by the interpreter.

One of the many uses of a symbol is as a function name. A valid function call is always a list with a symbol as its first s-expression.

What happens if you use something else?

(
(tetris)
)
(123)
*scratch*

This time, the Invalid function error explains itself.

We can now evaluate four kinds of s-expression:

There’s an exception to the rule — defun.

Recall the early definition of xx‑view.

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

Th defun function has the arguments xx‑view, () and (coe‑xx‑basket‑view). Unlike a normal function, these arguments aren’t evaluated.

defun is a special form. Special forms are functions with their own evaluation mechanisms built into the interpreter. Instead of evaluating its arguments, defun uses them to load a function into the environment.

Calls to defun follow a specific structure:

(defunNAME
(ARG...)
BODY...)

Elisp has many special forms, each with its own custom structure. The interpreter is incoherent when this structure isn’t followed and its error message, if any at all, will likely cause more confusion than clarity.

The special forms throughout this book are coloured green to distinguish them from usual functions. Don’t let their colour deceive you — they are difficult beasts, only tamed with practice.

As you may have gathered, defun isn’t the only special form. We’ll meet the let and if forms shortly.

These definitions are simple enough, but no doubt s-expressions are still a little mysterious to you. Are they code, data, or simply text? For that matter, what are symbols? Why do we call + a symbol at one point and a function the next?

To understand these distinctions we must step back and find a new perspective.

Several perspectives, in fact.

Recall that there are two stages to the interpreter: reading followed by evaluation. The interpreter interprets “code” in a general sense, but the code in each of these stages is thought of differently.

Reading

The read stage produces an s-expression; a structure composed of lists, symbols, strings and numbers. One may view it as data without meaning. Nothing can be termed a function at this point because the interpreter doesn’t act on it.

Browse the structure of the s-expression below using your arrow keys.

Select an s-expression:

(*
(+43)
(length"Hello!")
)

Evaluation

Subsequently, we have evaluation. The interpreter walks through the s-expression, looking up symbols and calling their respective functions.

Step through the evaluation of the s-expression below.

Evaluate the list …

(*
(+43)
(length"Hello!")
)

* is a function.

(*
(+43)
(length"Hello!")
)

Evaluate the list …

(*
(+43)
(length"Hello!")
)

+ is a function.

(*
(+43)
(length"Hello!")
)

Evaluate4 to 4

(*
(+43)
(length"Hello!")
)

Evaluate3 to 3

(*
(+43)
(length"Hello!")
)

Evaluate the list to 7

(*7
(length"Hello!")
)

Evaluate the list …

(*7
(length"Hello!")
)

length is a function.

(*7
(length"Hello!")
)

Evaluate"Hello!" to "Hello!"

(*7
(length"Hello!")
)

Evaluate the list to 6

(*76)

Evaluate the list to 42

42

At this point, the symbols *, + and length have additional meaning — we must consider them functions to describe evaluation. Similarly, 4, 3 and "Hello!" are arguments.

The interpreter and you

It would be remiss not to mention one more perspective. Programming is a discourse between the computer and the human, and the more usual way of reading code is by following the programmer’s intent. The reading guides at the end of each chapter demonstrate this.

These distinctions then — code and data, symbols and functions — are made clear through the lens of the interpreter. At this point, they may seem pedantic and irrelevant to you. In simple languages, the programmer is only concerned with conveying intent, and they only delve into evaluation when they find bugs.

Elisp is different. We live far too close to the interpreter to comfortably ignore its workings. Its perspectives need to be explored.

Enough of studying the s-expressions. Let’s use them.

Open a new file with M‑x find‑file xx.el and write a bare-bones xx command.

Remember to evaluate it with M‑x eval‑defun.

(defunxx
()
(interactive)
)
xx.el

You can now call M‑x xx, but it won’t do anything yet.

Create a *xx* buffer with get‑buffer‑create. Pass it a "*xx*" string for the buffer name.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
)
xx.el

This seems not to do anything either. Did it really create a buffer?

Use M‑x list‑buffers to check there is a *xx* buffer.

It would be useful to view it.

View *xx* in a window using switch‑to‑buffer.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
)
xx.el

How many *xx* buffers are there?

Buffers are uniquely identified by their names, so get‑buffer‑create only creates a buffer if one does not already exist.

Write text to the buffer with insert.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(insert"Skeins")
)
xx.el

Call M‑x xx.

Skeins
*xx*

All is well so far.

Call M‑x xx twice more.

SkeinsSkeinsSkeins
*xx*
M‑x xx 

Much as we need skeins, this isn’t quite what we’re after.

Use erase‑buffer to wipe away the old text.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(insert"Skeins")
)
xx.el

Add some more text, inserting line breaks with newline.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(insert"Skeins")
(newline)
(insert"====")
(newline)
(insert"No. strands: ")
(newline)
(insert"No. stitches: ")
(newline)
(insert"No. skeins: ")
(newline)
)
xx.el

Call M‑x xx.

Skeins
======
No. strands:
No. stitches:
No. skeins:
*xx*
M‑x xx 

A true Elisper would weep.

We’ve duplicated insert and newline five times. If only we had an function to do this instead.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"====")
(xx‑‑insert‑line"No. strands: ")
(xx‑‑insert‑line"No. stitches: ")
(xx‑‑insert‑line"No. skeins: ")
)
xx.el

xx‑‑insert‑line inserts some text and a new line. It doesn’t exist, but we can write it.

Write a xx‑‑insert‑line function that accepts a single text argument.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"====")
(xx‑‑insert‑line"No. strands: ")
(xx‑‑insert‑line"No. stitches: ")
(xx‑‑insert‑line"No. skeins: ")
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
xx.el

This function accepts one argument — the argument list contains a single text symbol. It doesn’t have to be named text; any name will do.

When we call the function, text is evaluated to the string we pass in.

We can get the number of stitches with the read‑string function.

This writes a prompt to the minibuffer and returns the string entered after it. Its result can be combined with other strings using concat.

Use read‑string to get the user input.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"====")
(xx‑‑insert‑line
(concat"No. strands: "
(read‑string"No. strands: ")
)
)
(xx‑‑insert‑line
(concat"No. stitches: "
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line"No. skeins: ")
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
xx.el

We’ll now write a xx‑‑num‑skeins function to calculate the number of skeins to buy.

This takes the number of stitches and strands used as arguments.

Write a bare-bones xx‑‑num‑skeins definition.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(xx‑‑insert‑line
(concat"No. strands: "
(read‑string"No. strands: ")
)
)
(xx‑‑insert‑line
(concat"No. stitches: "
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line"No. skeins: ")
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
)
xx.el

Elisp has several functions for working with numbers.

Let’s assume that each cross takes 1cm of thread to make.

A skein contains six strands and is eight metres long, so contains 4800cm of thread in total.

To calculate the number of skeins we need; we must multiply the number of stitches by the number of strands, divide by 4800 and round up.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(xx‑‑insert‑line
(concat"No. strands: "
(read‑string"No. strands: ")
)
)
(xx‑‑insert‑line
(concat"No. stitches: "
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line"No. skeins: ")
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
(ceiling
(/
(*strandsstitches)
4800)
)
)
xx.el

Our pattern has six hundred stitches.

How many skeins does this give for six hundred stitches at four strands?

Evaluate (xx‑‑num‑skeins 4 600) in the *scratch* buffer.

Our cursory check shows that something’s amiss.

If you’ve programmed before, you might have an inkling of what’s wrong. Nevertheless, working through it is a good exercise in troubleshooting.

Write out the full expression in the *scratch* buffer and evaluate each list with M‑x eval‑last‑sexp.

Which result is unexpected?

(ceiling
(/
(*4600)
4800)
)
*scratch*
M‑x eval-last-sexp 

Surprisingly, (/ (* 600 4) 4800) gives 0 instead of 0.5.

Elisp has two kinds of numbers: 600 and 4 are integers, however 0.5 is a decimal, termed a float.

When provided with integer arguments, / returns their integer quotient. (/ 2400 4800) is the number of times 4800 fits into 2400, which is 0.

What is (/ 2400.0 4800.0)?

We must pass / floats to perform decimal division.

Convert the integers to floats using the float function.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(xx‑‑insert‑line
(concat"No. strands: "
(read‑string"No. strands: ")
)
)
(xx‑‑insert‑line
(concat"No. stitches: "
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line"No. skeins: ")
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
(ceiling
(/
(float
(*strandsstitches)
)
4800.0)
)
)
xx.el

Verify that (xx‑‑num‑skeins 4 600) calculates a single skein.

Calling our function from xx poses a problem.

Attempt to use xx‑‑num‑skeins.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(xx‑‑insert‑line
(concat"No. strands: "
(read‑string"No. strands: ")
)
)
(xx‑‑insert‑line
(concat"No. stitches: "
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line
(concat"No. skeins:  "
(xx‑‑num‑skeins)
)
)
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
(ceiling
(/
(float
(*strandsstitches)
)
4800.0)
)
)
xx.el

We need to pass it the user inputs from read‑string, but also need to write these to the buffer. The inputs need to be referred to in multiple places.

The let special form facilitates this.

An example

We’ve assumed that the thread length per stitch is 1cm. This is an approximation based on the size of a square in the canvas used.

A
diagram of a cross stitch

A cross is formed by pulling the thread diagonally through the front of the canvas, then behind along the side, diagonally through the front again, and finally diagonally along the back.

The length of thread used to make a stitch can be expressed as:

(+diagonalsidediagonaldiagonal)

Suppose your canvas square has 0.25 cm sides. You can calculate the length using let.

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
*scratch*

Evaluate the let call in the *scratch* buffer.

This code snippet is a bit complex. Let’s walk through it step by step.

Step through the evaluation of let.

Evaluate the list …

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

let is a special form.

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate the bindings …

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate0.25 to 0.25

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate the list …

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

* is a function.

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate1.4 to 1.4

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate0.25 to 0.25

(let
(
(side0.25)
(diagonal
(*1.40.25)
)
)
(+diagonalsidediagonaldiagonal)
)
...

Evaluate the list to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
...

Bind side to 0.25

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
side
variable
0.25
...

Bind diagonal to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluate the body …

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluate the list …

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

+ is a function.

(let
(
(side0.25)
(diagonal0.35)
)
(+diagonalsidediagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluatediagonal to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+0.35sidediagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluateside to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+0.350.25diagonaldiagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluatediagonal to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+0.350.250.35diagonal)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluatediagonal to 0.35

(let
(
(side0.25)
(diagonal0.35)
)
(+0.350.250.350.35)
)
side
variable
0.25
diagonal
variable
0.35
...

Evaluate the list to 1.3

(let
(
(side0.25)
(diagonal0.35)
)
1.3)
side
variable
0.25
diagonal
variable
0.35
...

Evaluate the list to 1.3

1.3
...

The thread used for a stitch is 1.3 cm.

The inner workings

let binds symbols to values and allows us to refer to those symbols within its body.

(let
(
(NAMEVALUE)
...)
BODY...)

As a special form, let has a custom evaluation strategy.

The interpreter evaluates the bindings by using its environment to store their values. Values stored within the environment are termed variables. The bound symbol is used as the variable name, and the result of the bound s-expression is stored as its value.

When the body is evaluated, the interpreter searches its environment for any symbols and substitutes their values.

side
variable
0.25
diagonal
variable
0.35
...

Experiments

We know how let works. It’s time to try and break it.

Attempt to use the symbol rhino that isn’t bound to a value.

(let
(
(x6)
)
(+xrhino)
)
*scratch*

The error Symbol’s value as variable is void: rhino is reasonable, if oddly worded.

We can think of more creative experiments. Can a variable have the same name as a function?

Bind + to the value 6.

(let
(
(+6)
)
(+++)
)
*scratch*

This is valid code, if highly obscure.

Function and variable names are stored separately in the environment. The interpreter looks up the function or variable depending on the symbol’s position in a list. The first + in (+ + +) refers to the + function, while the last two refer to 6.

+
function
instructions...
variable
6
...

Do the bindings have to be independent?

Evaluate a let form with one binding calculated from another.

(let
(
(side0.25)
(diagonal
(*1.4side)
)
)
(+diagonalsidediagonaldiagonal)
)
*scratch*

The interpreter complains that side doesn’t have a variable: the binding for diagonal cannot depend on it. This is the most obvious limitation of the let form. We’ll overcome it in the next chapter. For now, independent bindings suit our needs.

Use let to refer to the user inputs.

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(let
(
(strands
(read‑string"No. strands: ")
)
(stitches
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line
(concat"No. strands: "strands)
)
(xx‑‑insert‑line
(concat"No. stitches: "stitches)
)
(xx‑‑insert‑line
(concat"No. skeins: "
(xx‑‑num‑skeinsstrandsstitches)
)
)
)
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
(ceiling
(/
(float
(*strandsstitches)
)
4800.0)
)
)
xx.el

Call M‑x xx with 600 stitches and 4 strands.

The interpreter prints a rather obscure error.

Wrong type argument: number-or-marker-p, "600"

Troubleshoot by writing the xx‑‑num‑skeins body in the *scratch* buffer. Substitute the arguments "600" and "4".

The culprit is the * function. It reasonably expects a number but we’ve given it the string "600".

Use the functions string‑to‑number and number‑to‑string to convert the arguments and result of xx‑‑num‑skeins .

(defunxx
()
(interactive)
(get‑buffer‑create"*xx*")
(switch‑to‑buffer"*xx*")
(erase‑buffer)
(xx‑‑insert‑line"Skeins")
(xx‑‑insert‑line"======")
(let
(
(strands
(read‑string"No. strands: ")
)
(stitches
(read‑string"No. stitches: ")
)
)
(xx‑‑insert‑line
(concat"No. strands: "strands)
)
(xx‑‑insert‑line
(concat"No. stitches: "stitches)
)
(xx‑‑insert‑line
(concat"No. skeins: "
(number‑to‑string
(xx‑‑num‑skeins
(string‑to‑numberstrands)
(string‑to‑numberstitches)
)
)
)
)
)
)
(defunxx‑‑insert‑line
(text)
(inserttext)
(newline)
)
(defunxx‑‑num‑skeins
(strandsstitches)
(ceiling
(/
(float
(*strandsstitches)
)
4800.0)
)
)
xx.el

Finally, let’s check that it works.

Call M‑x xx with 600 stitches and 4 strands.

If all goes well, you should only need a single skein.

Skeins
======
No. strands: 4
No. stitches: 600
No. skeins: 1
*xx*

We’re printing the whole number of skeins. This is useful for purchasing skeins, but makes it hard for an artist to use up leftover thread. Modify the code to output a fractional number of skeins instead.

A cross-stitch pattern specifies a canvas size in “counts”, or squares per inch. Expand xx to ask for the squares per inch, and use this in xx‑‑num‑skeins to calculate the thread length per stitch.

An artist will typically cut their thread into 50cm pieces. Around 5cm of this is for securing the beginning and end stitches. Modify xx‑num‑skeins to account for 5cm of waste per 45cm of used thread.