Skip to main content

Welcome to a Grasp lesson

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

Lesson 6

S-expressions: Structure and Notation

Recommended resources

Introduction

Welcome to the second module, "Thinking in S-Expressions." In our last lesson, we concluded our tour of programming fundamentals by defining data structures. You learned that they are ways to organize collections of data, and that in Lisp, the list is the most important data structure of all, serving to represent both data and code.

This lesson takes the next logical step. If lists are so central, how do we write them? How do we write code? In Lisp, the answer to both questions is the same: with Symbolic Expressions, or S-expressions. They are the fundamental syntax of Emacs Lisp.

Your learning outcome for this lesson is to: Explain the structure of an S-expression and how prefix notation differs from infix notation. By the end of this lesson, you will understand the basic grammar of Elisp, preparing you to write and evaluate your first pieces of code in our next session.


1. The Anatomy of an S-Expression

The term "S-expression" is short for "Symbolic Expression." It is the one and only syntactic form in Lisp. Everything—code, data, configurations—is written as an S-expression.

This uniformity is a core part of the Lisp philosophy you're interested in. An S-expression has a simple, recursive definition.

Introduction to Emacs 3 - On Emacs-Lisp

The article 'Introduction to Emacs 3 - On Emacs-Lisp' provides a clear, formal definition of S-expressions. Understanding this is key to everything that follows.

Please read the section titled 'S-Expressions'. Focus on the two-part definition: an S-expression is either an atom or a list of other S-expressions. Don't worry about 'dotted-pair notation' for now; we'll return to it in a later lesson. The key is to grasp the distinction between atoms (indivisible values like numbers or symbols) and lists (collections in parentheses).

As the text explains, S-expressions come in two flavors:

  1. Atoms: These are the fundamental, indivisible units of the language. They cannot be broken down further. Examples include:

    • Numbers: 42, 3.14
    • Strings: "Hello, world!"
    • Symbols: message, t, my-variable (Symbols are similar to identifiers or variable names in other languages).
  2. Lists: A sequence of other S-expressions (which can be atoms or other lists) enclosed in parentheses (). For example: (+ 1 2) is a list containing three atoms: the symbol +, the number 1, and the number 2.

This simple structure is all there is. The complexity and power of Lisp emerge from combining these basic building blocks.


2. Prefix vs. Infix Notation: A New Way of Writing

If you've encountered programming or even standard mathematics, you're used to infix notation, where the operator sits in between its operands:
2 + 3

Lisp takes a different approach called prefix notation (or Cambridge Polish Notation). The operator is moved to the front of the list, becoming the first element inside the parentheses:
(+ 2 3)

This might seem strange at first, but it is one of Lisp's most powerful features. A list intended to be executed as code is interpreted as a function call.

  • The first element is the function or operator.
  • All subsequent elements are the arguments passed to that function.

Let's watch a short video that demonstrates this concept clearly.

Emacs-Lisp Basics | Syntax, Variables, Functions and Lists | Switching to Emacs #6.1

The video 'Emacs-Lisp Basics' by Augusto provides a great demonstration of prefix notation and how Elisp interprets S-expressions as function calls.

Watch from 05:34 to 07:44. Pay close attention to how the video first shows the incorrect way to sum two numbers (using infix style) and then corrects it to the proper Lisp prefix notation. This highlights the core difference in thinking.

The key insight is that an S-expression like (+ 2 3) is not just a different way of writing math. It's a unified syntax for any operation. The same structure is used to add numbers, manipulate text, or open a file.

For a concise, text-based reinforcement of this idea, please read the following excerpt.

LISP

This resource from Gordon College's computer science department provides a very direct definition of Lisp's prefix notation.

Read the short section titled 'LISP Uses a Variant of Prefix Notation'. It gives another example of how a more complex infix expression is translated into Lisp's parenthesized prefix form.

One major advantage of prefix notation is that it's unambiguous. The parentheses clearly define the scope of the operation, and the function to be called is always in the first position. This consistency simplifies both the computer's job of parsing the code and, once you are used to it, the programmer's job of reading it.


3. Building Complexity Through Nesting

The true power of S-expressions becomes apparent when you nest them. Since any argument to a function can itself be another S-expression, you can build up complex operations from simple parts.

When the Lisp interpreter encounters a nested S-expression, it evaluates from the inside out. The result of the innermost expression becomes an argument for the expression that contains it.

Consider this example: (* (+ 1 2) 10).

  1. The interpreter first evaluates the inner expression (+ 1 2), which results in 3.
  2. This result, 3, replaces the inner S-expression, effectively transforming the code to (* 3 10).
  3. The interpreter then evaluates this final expression, resulting in 30.

The video you watched earlier briefly touched on this. Let's revisit that specific segment.

Emacs-Lisp Basics | Syntax, Variables, Functions and Lists | Switching to Emacs #6.1

This clip from the 'Emacs-Lisp Basics' video shows a simple, practical example of nesting S-expressions.

Watch from 06:38 to 07:31. The video demonstrates evaluating (+ (* 42 2) 42). Notice how the multiplication (* 42 2) must be resolved before the addition can be performed.

To help visualize this evaluation process, it's useful to think of a nested S-expression as a tree. This is a mental model many programmers use to understand the structure of code.

Introduction to Emacs 3 - On Emacs-Lisp

The article 'Introduction to Emacs 3 - On Emacs-Lisp' provides excellent tree diagrams to visualize nested S-expressions. This will help you build an intuitive understanding of evaluation order.

Return to the 'S-Expressions' section you read earlier. This time, focus on the part that starts with 'As you can probably see by now...'. Study the tree visualizations for the expressions (* (+ 1 2) (+ 3 4)) and the more complex one that follows. This directly illustrates the 'inside-out' evaluation process.

This tree-like structure is not just a visualization aid; it reflects the underlying data structure—a list of lists—that Lisp uses to represent the code. This is a direct consequence of the "code as data" paradigm.


Conclusion

This lesson introduced you to the fundamental syntax of Emacs Lisp. You have moved from the abstract concept of a list to its concrete representation as an S-expression.

Key Takeaways:

  • S-expressions are the building blocks of Lisp, representing both code and data.
  • An S-expression is either an atom (an indivisible value like a number or symbol) or a list (a parenthesized sequence of other S-expressions).
  • Lisp uses prefix notation, where the operator/function is the first element in a list, e.g., (+ 1 2). This contrasts with the infix notation common in mathematics, e.g., 1 + 2.
  • Nesting S-expressions allows for building complex computations, which are evaluated from the inside out.

Preview of the Next Lesson:
Now that you understand the structure of an S-expression, it's time to bring it to life. In the next lesson, you will get your hands on the keyboard and learn how to use Emacs itself as an interactive environment to write and evaluate your first simple S-expressions. We will explore the *scratch* buffer and the "Interactive Elisp Mode" (ielm), turning theory into practice.