Getting Started with Emacs Lisp: Core Concepts Explained
Emacs Lisp (often abbreviated as Elisp) is the programming language that powers the extensibility of the Emacs text editor. It’s a dialect of Lisp that has been adapted specifically for text editing tasks. While it might look unusual at first glance, understanding a few key concepts opens up a world of possibilities for customizing your Emacs experience.
In this article, we’ll explore some of the fundamental concepts in Emacs Lisp that often confuse newcomers.
Variables: Global vs Local Scope
Elisp provides several ways to define variables, each with different scoping behaviors.
setq
: Global Variables
The setq
function is used to define and set global variables:
(setq my-variable "Hello, world!")
These variables are accessible from anywhere in your Emacs session once defined. They’re ideal for configuration settings or values that need to persist.
let
and let*
: Local Variables
For variables with limited scope, Elisp provides let
and let*
:
;; Using let for local binding
(let ((x 5)
(y 10))
(message "Sum: %d" (+ x y)))
;; x and y are not accessible here
The difference between let
and let*
is significant:
let
evaluates all variable expressions before bindinglet*
evaluates in sequence, allowing later variables to reference earlier ones
;; This won't work with let
(let ((x 5)
(y (+ x 2))) ; Error: x is not yet bound when this is evaluated
(message "y: %d" y))
;; This works with let*
(let* ((x 5)
(y (+ x 2))) ; Works because x is already bound
(message "y: %d" y)) ; Output: "y: 7"
Understanding Quote ('
)
One of the most distinctive features of Lisp is the quote character ('
). It prevents evaluation of an expression, instead treating it as data.
Quoting Variables
When you place a quote in front of a symbol:
'my-symbol
You’re telling Elisp, “Don’t evaluate this symbol, just give me the symbol itself.” This is similar to references in other programming languages.
Without the quote, Elisp would try to look up the value of the variable my-symbol
.
List Syntax: '(...)
vs (list ...)
and (cons ...)
Lists are fundamental data structures in Lisp. There are several ways to create them:
;; These are equivalent:
'(a b c)
(list 'a 'b 'c)
;; These are also equivalent:
'(a . b)
(cons 'a 'b)
The quoted form '(...)
is concise but has a limitation: everything inside is automatically quoted. This means you can’t mix evaluated and non-evaluated expressions.
When to use each:
- Use
'(...)
for literal lists that don’t need evaluation - Use
(list ...)
when you need to mix literals and evaluated expressions:
(setq name "John")
(list 'person name 'age 30) ; => (person "John" age 30)
Understanding Cons Cells: CAR and CDR
At the heart of Lisp’s data structures is the “cons cell” - a simple pair of values. Each cons cell has two parts, traditionally accessed with the functions car
and cdr
(pronounced “could-er”):
(setq my-pair (cons 'a 'b))
(car my-pair) ; => a
(cdr my-pair) ; => b
These peculiarly named functions are historical artifacts from the original Lisp implementation on IBM machines:
car
: Contents of the Address part of Registercdr
: Contents of the Decrement part of Register
In modern terms, think of them as:
car
: head (first element)cdr
: tail (rest of the list)
Lists in Lisp are actually chains of cons cells that always terminate with nil
:
;; This list:
'(1 2 3)
;; Is actually structured as:
(cons 1 (cons 2 (cons 3 nil)))
Visualized as pairs:
(1 . (2 . (3 . nil)))
This implicit nil
at the end is crucial - it’s what defines a “proper list” in Lisp. Without this nil termination, you would have a different data structure. The presence of this nil terminator is what allows functions like length
to work properly and what lets Lisp know where a list ends.
You can extract elements from lists using these functions:
(setq numbers '(1 2 3 4))
(car numbers) ; => 1
(cdr numbers) ; => (2 3 4)
(car (cdr numbers)) ; => 2
Elisp provides convenient shorthand functions like cadr
(car of cdr) for common combinations:
(cadr numbers) ; => 2 (same as (car (cdr numbers)))
(caddr numbers) ; => 3 (car of cdr of cdr)
The Dot Syntax in Pairs
In Emacs Lisp, you might encounter constructs with dot notation like:
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))
This dot notation represents a “cons cell” or pair. In this example, we’re creating a pair where the car is "\\.md\\'"
and the cdr is markdown-mode
.
The following expressions are equivalent:
'("\\.md\\'" . markdown-mode)
(cons "\\.md\\'" 'markdown-mode)
The dot creates a direct pair between two values. This syntax is commonly used in alists (like auto-mode-alist
), mode hooks, and other paired data structures in Emacs.
When you see a dot in the middle of a list, it means “the rest of this list is the cdr of this cons cell” rather than continuing the list structure with an implicit nil at the end.
To understand the difference clearly:
;; A list with two elements (has an implicit nil at the end)
'(1 2) ; => equivalent to (cons 1 (cons 2 nil))
;; A single cons cell (a pair) - NOT a proper list
'(1 . 2) ; => equivalent to (cons 1 2)
The difference is significant:
(length '(1 2)) ; => 2
(length '(1 . 2)) ; => Error: Wrong type argument: listp, (1 . 2)
This distinction is fundamental to understanding how Lisp data structures work.
Function Quoting: #'
vs '
In Elisp, you’ll see both 'function-name
and #'function-name
when referring to functions. The difference is important:
'function-name
simply quotes the symbol#'function-name
is shorthand for(function function-name)
, which specifically tells Elisp that the symbol represents a function
When should you use #'
? Best practice is to use it whenever you’re referring to a function as a function, especially with higher-order functions:
;; Good practice
(mapcar #'1+ '(1 2 3)) ; => (2 3 4)
;; Works but less explicit
(mapcar '1+ '(1 2 3))
Using #'
helps the byte-compiler optimize your code and makes your intent clearer to other programmers.
Association Lists (alists)
Association lists (alists) are lists of key-value pairs used for simple lookups:
(setq my-alist '((name . "John")
(age . 30)
(city . "Boston")))
You can access values using functions like assoc
:
(cdr (assoc 'age my-alist)) ; => 30
Alists are frequently used in Emacs for configuration options, especially when the list is relatively small and frequently modified. They’re simple but not as efficient for large datasets.
Property Lists (plists)
Property lists are another key-value structure in Elisp, but with a different syntax:
(setq my-plist '(:name "John" :age 30 :city "Boston"))
Notice how the keys are prefixed with colons. These are called keywords, similar to Ruby’s symbols. You can access values using plist-get
:
(plist-get my-plist :age) ; => 30
Other languages with similar concepts include:
- Ruby with its symbols (
:symbol
) - Clojure with keywords (
:keyword
) - Elixir with atoms (
:atom
)
Plists are often used for function arguments that have default values or are optional.
Summary
Understanding these fundamental Emacs Lisp concepts will help you:
- Write more effective Elisp code
- Understand existing Emacs configurations
- Create your own Emacs extensions
Emacs Lisp’s power comes from its simplicity and consistency. Once you grasp these basic concepts, you’ll find that the language follows logical patterns that make learning advanced features much easier.
Happy hacking!