This post outlines a recipe for designing functions in Emacs Lisp which is based on a superb book, How To Design Programs by M. Felleisen, R. B. Findler, M. Flatt and S. Krishnamurthi. The following paragraphs transplant the material and the design recipe from Part 1, Section 3.1 Designing Functions from Racket to Emacs Lisp. I read this book almost two years ago, while it was still being written. Techniques described in it had an enormous impact on how I think about information, data and programming. I encourage everyone interested in the thinking process behind designing programs to read it. It’s truly a gem.

I came to writing this post because I’m getting into Emacs Lisp development and wanted to review how the syntax from Racket will map to Emacs Lisp.

1. Express how you wish to represent information as data

Usually, a one-line comment is enough.

;; We use numbers to represent centimetres
;; List of Numbers represents the height of a plane per unit of time

2. Write down a signature, a statement of purpose, and a function header

In the context of Emacs Lisp and its conventions we’ll be using docstring to store this information. This is a take-while function with a sample docstring.

(defun take-while (pred list)
  "Produces a new list of successive items from LIST while (PRED item) is non-nil.

Returns a list of the same type as LIST.

PRED is a function of one argument that returns nil or non-nil value

LIST is a list of elements of the same type PRED accepts"
  ...)

A function signature is a comment which describes the inputs your function takes: how many of them are and types of data they represent. We’ll be using the third and subsequent lines of the docstring for this purpose. Lines relate to function parameters in order and we use one line per parameter. In e.g. Haskell, the same signature would be expressed as (A -> Bool) -> [A] -> [A].

(...
  "...
Returns a list of the same type as LIST.

PRED is a function of one argument that returns nil or non-nil value

LIST is a list of elements of the same type PRED accepts"
...)

The statement of purpose is a single line which is basically an answer to the question: what does the function compute?. We’ll write the purpose statement on the first line of the docstring.

(...
  "Produces a new list of successive items from LIST while (PRED item) is non-nil.
..."
...)

A function header is a simple function definition, also called a stub. It’s a function which won’t throw an error when we run it, but it won’t return expected values which agree with its purpose statement for most inputs. It will return however one of the possible values.

(defun take-while (pred list)
  "Produces a new list of successive items from LIST while (PRED item) is non-nil.

Returns a list of the same type as LIST.

PRED is a function of one argument that returns nil or non-nil value

LIST is a list of elements of the same type PRED accepts"
  '())

Often it is useful to clarify what is computed, as we did above. We can add more detail to the second line and mention additional constraints or behaviours of the function.

At this stage, the function is well formed and should not cause any errors when called. The output might not be very interesting though, it’s a hard-coded value.

3. Illustrate the signature and the purpose statement with some functional examples

To construct a functional example, pick one piece of data from each function argument and determine what you expect back.

(defun take-while (pred list)
  "..."
  ;; given (lambda (x) (< 5) '()), expect '()
  ;; given (lambda (x) (> 2) '(1 2 3)), expect '()
  ;; given (lambda (x) (< 2) '(1 2 3)), expect '(1)
  ;; given (lambda (x) (< 5) '(1 2 3)), expect '(1 2 3)
  ...)

4. Take the inventory

This step helps us to understand what we can work with (inputs) and what we need to compute (outputs). For simple functions, the data comes only through function parameters. For more complex it could also come from arguments of functions wrapping or other contexts (like e.g. global constants and variables). Parameters are placeholders for values we don’t know. However, we know the type of data coming our way which is needed to compute the result. To remind ourselves of what we have we replace the function body (the stub) with the template. The template mentions each parameter the function takes:

(defun take-while (pred list)
  "..."
  (... pred ...)
  (... (car list) ...)
  (... (cdr list) ...))

The dots remind us that this function is incomplete, but the template shows what we can work with to fulfil the purpose of the function. In simpler functions we could’ve just added a (... list ...), however, we know that we want to look at each element of the list, therefore it’s convenient to deconstruct complex structures into smaller already at this stage. So we take the first element of the list (... (car list) ...) and the rest of the list (... (cdr list) ...).

5. Build the function

Now is the time to code. We will replace the function’s body with an expression which attempts to fulfil what the purpose statement promises.

(defun take-while (pred list)
  "..."
  ;;  ...
  (cond ((null (car list)) '())
        ((not (funcall pred (car list))) '())
        (t (cons (car list) (take-while pred (cdr list))))))

6. Test

The last step of proper design is to test the function on the examples we’ve defined before. Emacs provides functions for testing out-of-the-box. We can use ERT built-in library, which stands for Emacs Regression Testing (C-h i and navigate to ERT). After we convert the examples, it’s OK to remove the comments. M-x ert runs the tests. Converted examples would look as follows:

(ert-deftest take-while-test ()
  (should (= (take-while (lambda (x) (> 2) '(1 2 3))) '()))
  (should (= (take-while (lambda (x) (< 2) '(1 2 3))) '(1)))
  (should (= (take-while (lambda (x) (< 5) '(1 2 3))) '(1 2 3)))
  (should (= ((lambda (x) (> 2) '(1 2 3))) '())))

The full function implementation with following the How To Design Functions recipe is below.

(defun take-while (pred list)
  "Produces a new list of successive items from LIST while (PRED item) is non-nil.

Returns a list of the same type as LIST.

PRED is a function of one argument that returns nil or non-nil value

LIST is a list of elements of the same type PRED accepts"
  (cond ((null (car list)) '())
        ((not (funcall pred (car list))) '())
        (t (cons (car list) (take-while pred (cdr list))))))

(ert-deftest take-while-test ()
  (should
    (equal (take-while (lambda (x) (> x 2)) '(1 2 3)) nil))
  (should
    (equal (take-while (lambda (x) (< x 2)) '(1 2 3)) '(1)))
  (should
    (equal (take-while (lambda (x) (< x 5)) '(1 2 3)) '(1 2 3)))
  (should
    (equal (take-while (lambda (x) (< x 2)) '(1 2 3)) '(1))))