Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 13

Evaluating Quoted Lists with 'eval'

Recommended resources

Introduction

In our last lesson, we explored the crucial difference between functions and special forms. We established that quote is a special form that allows us to prevent evaluation, effectively turning a piece of code into a simple data structure—a list. This is the first half of Lisp's famous "code is data" philosophy.

Today, we complete the circle. This lesson focuses on the eval function, the counterpart to quote. Your learning outcome is to use eval to explicitly evaluate a quoted list as code. We will take the very data structures we created with quote and instruct the Lisp interpreter to execute them. Understanding this mechanism is the key to unlocking dynamic code generation, which is a cornerstone of advanced Emacs customization.


1. From Data Back to Code

Let's start in your *scratch* buffer. In the previous lesson, you saw that evaluating the following expression results in a list:

'(+ 10 20)

When you evaluate this (using C-x C-e), Emacs simply returns the list (+ 10 20) in the minibuffer. The quote ' has done its job: it has prevented the evaluation of the + function.

Now, what if we have this list and we want to perform the addition? This is where eval comes in. eval is a function that takes one argument and evaluates it as a Lisp expression.

Try this:

(eval '(+ 10 20))

Evaluating this expression yields 30.

Let's break down what happened:

  1. The eval function is called.
  2. Its argument is the quoted list '(+ 10 20), which evaluates to the literal list (+ 10 20).
  3. eval takes this list and passes it to the Lisp interpreter, which executes it as a function call.
  4. The interpreter sees + as the function and 10 and 20 as the arguments, performs the addition, and returns 30.

You have just successfully treated code as data ('(+ 10 20)) and then explicitly instructed Emacs to treat that data as code.

ElispCheatSheet | Quick reference to the core language of Emacs

The ElispCheatSheet provides a very direct and clear explanation of this relationship between quoting and evaluating.

Read the section titled 'Quotes, Quasi-Quotes, and Unquotes'. Focus on the paragraphs that describe how quote forbids evaluation and how eval 'executes code that is in data form; i.e., is quoted.' The examples provided there are excellent summaries.


2. The Evaluator and the 'eval' Function

To really understand eval, it's helpful to formalize what "evaluation" is. The program inside Emacs that computes the value of an expression is called the Lisp interpreter, or simply the evaluator. When you press C-x C-e, you are invoking this evaluator on the S-expression before your cursor.

The eval function is our direct, programmatic access to this evaluator. However, there's a crucial detail to remember: eval is itself a function. And like all functions (but unlike special forms), it evaluates its arguments before its own logic is executed. This can sometimes feel like a "double evaluation."

Let's study the official documentation to see this in action.

GNU Emacs Lisp Reference Manual - Evaluation

The GNU Emacs Lisp Reference Manual provides the definitive explanation of eval and its behavior.

Read the section titled 'Eval'. Pay close attention to the example involving the variables foo and bar. It perfectly illustrates the 'double evaluation' I mentioned.

Let's walk through that manual's example carefully. It's a classic demonstration of how evaluation works.

First, set up the variables in your *scratch* buffer and evaluate each line:

(setq foo 'bar)  ; The variable foo now holds the symbol bar
(setq bar 'baz)  ; The variable bar now holds the symbol baz

Now, consider the two calls to eval:

  1. (eval 'foo)

    • The Lisp evaluator sees the expression (eval 'foo).
    • It's a function call, so it first evaluates the argument 'foo. The result of evaluating 'foo is the symbol foo.
    • Now it calls eval with the symbol foo: (eval foo).
    • Inside eval, the symbol foo is evaluated again. The value of the variable foo is the symbol bar.
    • The final result is bar.
  2. (eval foo)

    • The evaluator sees (eval foo).
    • It evaluates the argument foo. The value of the variable foo is the symbol bar.
    • Now it calls eval with the symbol bar: (eval bar).
    • Inside eval, the symbol bar is evaluated. The value of the variable bar is the symbol baz.
    • The final result is baz.

This distinction is fundamental. Most of the time, you will be passing a quoted expression to eval to ensure you are evaluating exactly the data structure you wrote.


3. Code as a Tree

You have a background in writing and creative work. Think of an S-expression as the script for a performance. The quoted form, like '(+ (* 2 3) 5), is the sheet music or script itself—a static data object. The eval function is the conductor or director who brings it to life, telling the performers (the functions like + and *) what to do and in what order.

From a computer science perspective, every S-expression can be visualized as a tree. The expression '(+ (* 2 3) 5) isn't just a flat list; it's a data structure representing a hierarchy of operations.

Syntax Tree for y = 4 * (2 + x)
This image shows a syntax tree for the expression `y = 4 * (2 + x)`. A Lisp S-expression is a direct textual representation of such a tree. For example, `'(+ 5 (* 2 3))` would represent a tree with `+` at the top, and `5` and `*` as its children. The `eval` function's job is to traverse this tree from the bottom up, computing the values at each node until it arrives at a single final value.

When we write '(+ 5 (* 2 3)), we are creating the data structure that represents this tree. When we call eval on it, we are telling the Lisp interpreter to walk the tree and compute the result.


4. A Classic Application: The REPL

Why is this eval function so important? It forms the core of one of programming's most powerful tools: the REPL (Read-Eval-Print Loop). The *ielm* (Interactive Elisp Mode) buffer you've used is a REPL. It:

  1. Reads the expression you type.
  2. Evaluates it using the eval function.
  3. Prints the result.
  4. Loops back to the beginning.

The elegance of Lisp is that you can write a basic REPL in a single line of code. This is a powerful demonstration of eval's role.

ElispCheatSheet | Quick reference to the core language of Emacs

The ElispCheatSheet again provides a beautiful, concise example showing eval at the heart of a REPL.

Look at the section 'read and print'. You don't need to understand everything, but focus on the line (loop (print (eval (read)))) and the accompanying explanation. It perfectly encapsulates the role of eval.

This tiny program (loop (print (eval (read)))) is a complete, working (though simplified) Lisp interpreter. It shows that eval is the engine that drives all execution. It's the bridge between data read from a user (or a file) and the actions the computer takes.


Conclusion

You have now closed the loop on Lisp's "code is data" paradigm, or homoiconicity. You can stop evaluation with quote to treat code as a list, and you can force evaluation of a list with eval to treat it as code. This ability to manipulate code as if it were any other data is what gives Lisp its unique power and extensibility.

Key Takeaways:

  • eval is a function that takes a Lisp object and evaluates it as code. It is the programmatic interface to the Lisp interpreter.
  • eval is the functional inverse of quote. (eval (quote form)) is the same as form.
  • Since eval is a function, its arguments are evaluated before eval is called. To evaluate a literal list like (+ 1 2), you must quote it: (eval '(+ 1 2)).
  • The REPL (Read-Eval-Print Loop) is a core programming concept, and eval is the "Eval" step, making it a fundamental part of interactive development in Lisp.

Preview of the Next Lesson:

So far, we have used eval on static, quoted lists that we wrote by hand. But the true power comes from building these lists dynamically. In the next lesson, we will learn how to construct a list piece by piece—perhaps combining fixed elements with the values of variables—and then use eval to execute our dynamically created command. This is the first step toward writing truly adaptive and intelligent Emacs extensions.