Programming in Emacs Lisp

Table of Contents

There are several aspects of emacs lisp that are peculiar when compared to many other modern languages.

Lisp-2

The same symbol can be used for a function and a variable simultaneously. (ref)

(defun foo () 1)        ;; function

(setq foo 2)            ;; variable

(foo)                   ;; => 1

foo                     ;; => 2

The reason why two definitions can exist simultaneously for a single symbol (in a single namespace) is that each symbol can be conceptualized as having a "function cell" and a "value cell". In the example below, we show how two separate functions can be represented by the same symbol. Note that when a function is assigned to the value cell, it must be invoked using different syntax.

(defun bar () 1)                ;; assign to function cell

(setq bar (lambda () 2))        ;; assign to value cell

(bar)                           ;; => 1

bar                             ;; => (lambda nil 2)

(funcall bar)                   ;; => 2

Deleting a variable or checking the existence of a variable requires that the function and value cells of the symbol are addressed separately.

(fmakunbound 'bar)              ;; => bar

(fboundp 'bar)                  ;; => nil

(bar)                           ;; => Symbol's function definition is void: bar

(makunbound 'bar)               ;; => bar

(boundp 'bar)                   ;; => nil

bar                             ;; => Symbol's value as variable is void: bar

There is an alternate (and perhaps unconventional) way to define and call functions that mirror the way we handle values.

(fset 'bar (lambda () 1))       ;; => (lambda nil 1)

(set 'bar (lambda () 2))        ;; => (lambda nil 2)

(funcall 'bar)                  ;; => 1

(funcall bar)                   ;; => 2

Dynamic scoping

An important thing to note is the dynamic scoping rules of traditional emacs lisp. Lexical scoping is permitted in some instances:

  • using lexical-let
  • set buffer-local variable lexical-binding to non-nil (since Emacs 24).

An example contrasting dynamic and lexical scoping is taken from the EmacsWiki. The example is modified to use cl-flet and store the function definition in the symbol f's function cell, rather its value cell. Note that the value of a free symbol in dynamic scoping is defined by the environment in which the function is invoked, and not in that which it is defined.

(require 'cl)

;; dynamic scoping
(let ((a 1))                    ; binding (1)
  (cl-flet ((f () a))
    (let ((a 2))                ; binding (2)
      (f))))
;; => 2

;; lexical scoping
(lexical-let ((a 1))            ; binding (1)
  (cl-flet ((f () a))
    (lexical-let ((a 2))        ; binding (2)
      (f))))
;; => 1

;; alternatively,
(setq lexical-binding t)        ; set for entire buffer
(let ((a 1))                    ; binding (1)
  (cl-flet ((f () a))
    (let ((a 2))                ; binding (2)
      (f))))
;; => 1
(setq lexical-binding nil)      ; original value

t vs. nil

Boolean values are t and nil. (ref)

All of the following statements are true. Note that 0 and negative values are also true, in contrast with many other languages.

(let ((a t))
  (if a "true" "false"))
;; => "true"

(let ((a 0))
  (if a "true" "false"))
;; => "true"

(let ((a -1))
  (if a "true" "false"))
;; => "true"

f is not false – there is no symbol named f defined in the language.

(let ((a f))
  (if a "true" "false"))
;; => Symbol's value as variable is void: f

Both of the following statements are false. nil is equivalent to the empty list.

(let ((a nil))
  (if a "true" "false"))
;; => "false"

(let ((a ()))
  (if a "true" "false"))
;; => "false"

Creating sequences by quote vs. list

quote (shorthand: ') accepts a single argument and returns the object (symbol or expression) unevaluated. list returns a sequence of evaluated objects. Quoted numbers evaluate to themselves so the following expressions are equivalent:

(mapcar '1+ (list 1 2)) ;; => (2 3)

(mapcar '1+ '(1 2))     ;; => (2 3)

However, for variables this is not the case.

(let ((a 1))
  (list a 2))
;; => (1 2)

(let ((a 1))
  '(a 2))
;; => (a 2)

'(a 1) evaluates to (list 'a '1):

(equal '(a 1) (list 'a '1)) ;; => t

Therefore, the following two expressions produce different results.

(let* ((a 1)
       (pair (list a 2)))
  (mapcar '1+ pair))
;; => (2 3)

(let* ((a 1)
       (pair '(a 2)))
  (mapcar '1+ pair))
;; => Wrong type argument: number-or-marker-p, a

Perhaps there is a better solution, but a call to eval should be necessary at some point for the latter example.

(let* ((a 1)
       (pair '(a 2)))
  (mapcar '1+ (eval (cons 'list pair))))
;; => (2 3)

Note that we can also use a backquote to partially evaluate select elements of a quoted list.

(let* ((a 1)
       (pair `(,a 2)))
  (mapcar '1+ pair))
;; => (2 3)

Lists

Building quoted lists:

(cons '+ '(1 2))
(append '(+) '(1 2))

Evaluating functions:

(funcall '+ 1 2)
(apply '+ (list 1 2))
(eval '(+ 1 2))

For the following examples, let us use this variable:

(setq triple (list 1 2 3))

Extracting elements (note lists are zero-indexed for nth):

(car triple)                            ;; => 1
(cdr triple)                            ;; => (2 3)
(butlast triple)                        ;; => (1 2)
(last triple)                           ;; => (3)
(car (last triple))                     ;; => 3
(car (reverse triple))                  ;; => 3
(nth (1- (length triple)) triple)       ;; => 3

Multiple assignment can be accomplished using destructuring-bind:

(let (x y z)
  (destructuring-bind (x y z) triple
    (+ y z)))
;; => 5

Other structures

Sequences include

  • lists (recursive structures)
  • vectors (1-D of type: vector, string, char-table, bool-vector)

Hash tables can be implemented as

  • association lists ("alist", ordered, possibly duplicate keys)
  • hash tables (unordered, unique entries, fast)

Generated by Org-mode 9.2 with Emacs 26