Emacs & Lisp

Emacs

The Common Lisp language

Data types

  • Numbers
    • fixnum, integer, float, long float, binary, octal, hex, any radix
  • Strings
    • "this is a string"
    • Escape using backslash as usual
    • There are no single-quoted strings or triple-quoted strings as in Python.
  • Symbols

    A Lisp type that Python does not have is symbol. You can consider a symbol to be a word or a tag. To write a symbol, begin with a quote '. Note that Lisp converts a symbol to upper case (by default) and that it is case insensitive.

    CL-USER> 'landmark
    LANDMARK
    CL-USER> 'aBcDeFg
    ABCDEFG
    CL-USER>
    
  • Other types

Syntax

Lisp has a uniform, fully-parenthesized, prefix syntax. Where you might write gcd(12, 15) in Python, you write (gcd 12 15) in Lisp. Note the two differences:

  1. Python uses a comma as a separator. Lisp uses whitespace.
  2. The Python operator is before the opening parenthesis. The Lisp operator is after it.

This may seem unfamiliar since the Python syntax is more similar to the syntax of arithmetic you have known since elementary school. Another aspect of Lisp syntax that may be unfamiliar is that it is completely uniform, even for arithmetic operators, so the Python expression (5 + 4) * (3 - 2) is written in Lisp as (* (+ 5 4) (- 3 2)). This takes a little getting used to but it does have a number of advantages.

One is that some functions can have variable arity.

CL-USER> (+ 3 4)
7
CL-USER> (+ 3 4 5)
12
CL-USER> (+ 3 4 5 6 7 8)
33
CL-USER> (+)
0
CL-USER>

Another is that we can more easily manipulate Lisp source code, both manually and automatically, in order to perform syntax transformation. This is much more powerful than it might seem at first sight. We will return to it later.

Evaluation

To evaluate an expression, Lisp first evaluates the operator of the expression. If the operator is a function it then recursively evaluates all of its arguments from left to right. The function is then applied to the values resulting from the evaluation of the arguments.

For example, the evaluate of (+ (* 2 3) (* 4 5)) proceeds as follows:

  1. The operator + is the addition function
  2. Evaluate (* 2 3). * is a function and 2, and 3 are already values. Carry out the multiplication to get the value 6
  3. Evaluate (* 4 5). Similarly to the previous step we get 20.
  4. Add 6 and 20 to return 26.

This is the Common Lisp Evaluation Rule for functions but note that not every operator is a function. Later we will see that expressions which have some operators, known as special forms or macros, evaluate differently.

Lists

Lisp gets its name from being a LISt Processing language. We can make a list using the list function. Just as any other function, it first evaluates its arguments. Then it makes a list of those values.

CL-USER> (list (+ 3 4) (* 5 6) "seven")
(7 30 "seven")

We see that a list is written just like a function call, with parentheses as delimiters and whitespace as separator. Compare this with Python's notation [7, 30, "seven"]. Also note that the list can be heterogeneous, it can contain values of different types. Indeed a list can contain another list.

CL-USER> (list (+ 3 4) (* 5 6) (list "seven" "eight"))
(7 30 ("seven" "eight"))

We will often see this kind of list structure in Lisp.

Earlier we used a quote ' to write a symbol. We can also use it to write list structure.

CL-USER> (list 'a 'b 'c)
(A B C)
CL-USER> '(a b c)
(A B C)

In both cases this is a list containing three symbols. In the second case the contents of the list remain unevaluated. We can see this more clearly in

CL-USER> '(a b (+ 3 4))
(A B (+ 3 4))

Nothing after the quote is evaluated right up to the matching right parenthesis. Indeed the above is equivalent to:

CL-USER> (list 'a 'b (list '+ 3 4))
(A B (+ 3 4))

Also note the distinction between the quoted plus, which is a symbol, and a bare plus which would be a function operator in that position.

t and nil

We write the empty list as a matching pair of parentheses with nothing between them.

CL-USER> ()
NIL

Note how Lisp writes this as NIL. Indeed nil evaluates to NIL

CL-USER> nil
NIL

In Lisp nil does double duty as the empty list and the false value.

Truthiness and Falsiness

Wherever Lisp expects a Boolean value, for example as the first argument of an if, it treats nil as false and any other value as true. Note how this behaviour differs from Python where all zero and empty values are treated as false, e.g. 0, 0.0, [], {}, etc.

Lisp has a full set of Boolean operators

(and (or nil 45)
     (or (not nil)
         "maybe"))

Variables

We can store values in variables using setf as follows

CL-USER> (setf days-in-a-week (+ 3 4))
; in: SETF DAYS-IN-A-WEEK
;     (SETF DAYS-IN-A-WEEK (+ 3 4))
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::DAYS-IN-A-WEEK
;
; compilation unit finished
;   Undefined variable:
;     DAYS-IN-A-WEEK
;   caught 1 WARNING condition
7
CL-USER>

Note how sbcl gives us a warning before returning the value stored. The warning is telling us that it's good practice to define a variable before setting it. We'll just ignore it for now and get back to that later.

Now that we have stored a value, we can use this variable in any expression. If Lisp encounters it, it will replace the variable by the value we stored there.

CL-USER> (* 52 days-in-a-week)
364

The Python equivalent would be

days_in_a_week = 7
52 * days_in_a_week
364

Note how the convention in Python is to use underscores as separators. In fact, Python would parse days-in-a-week as the subtraction days - in - a - week. The convention in Lisp is to use dashes as separators. Indeed, because of the simplicity of Lisp's syntax, you can use many other characters in variable names and in symbols. For example, in Lisp this is just a symbol:

CL-USER> 'a*x^2+b*x+c
A*X^2+B*X+C

List operators

If you have a list, you can construct a new list consisting of a value followed by the elements of the list as follows.

CL-USER> (cons 'a '(b c d))
(A B C D)

We can retrieve the first element of a list and the rest of the list as follows:

CL-USER> (first '(a b c d))
A
CL-USER> (rest '(a b c d))
(B C D)

For historical reasons first and rest are also known as car and cdr.

CL-USER> (car '(a b c d))
A
CL-USER> (cdr '(a b c d))
(B C D)

A range of functions of the form cadadr are also available for digging more deeply into list structure.

CL-USER> (cadadr '(a (b c) d e))
c

This translates to

CL-USER> (car (cdr (car (cdr '(a (b c) d e)))))
c

We can ask whether something is a list with listp. The p at the end stands for predicate, i.e. something that returns a Boolean. It is a convention in Lisp to end a predicate with p or -p. Compare this to Racket's convention of ending a predicate with a question mark.

CL-USER> (listp '(a b c))
T
CL-USER> (listp (list 1 2 3))
T
CL-USER> (listp "nope")
NIL

We can ask whether a list is empty using null

CL-USER> (null ())
T
CL-USER> (null nil)
T
CL-USER> (null '(1 2 3))
NIL

Indeed, null is an exception to the above convention.

Defining functions

We have seen how to store values in variables. How do we define a function? Let's jump straight in with a numerical function.

(defun fact (n)
  "The factorial function"
  (if (zerop n)
      1
      (* n (fact (- n 1)))))

This is a definition of the factorial function. Racket programmers will note that it's almost identical to what one might write in Racket. The only differences are defun for define and the function name outside the parentheses. Let's look at it piece by piece.

  1. After defun is the name of the function being defined: fact
  2. It is followed by a list of parameters. This function will take a single argument named n.
  3. The second line contains a(n optional) documentation string. This is just like in Python except that a multi-line string in Lisp is still delimited by single double quotes.
  4. The if expression takes three arguments; the first is the test, the second the true case and the third the false case.
  5. If is an expression, not a statement. It returns either the value of its second argument or the value of its third argument, depending on the value of its first argument. In Python if is a statement; it does not return anything.
  6. No explicit return is required.

Expressions

  • In Python you have expressions and statements.
  • This is an expression:

    x = 3 + 4 * math.sqrt(69)
    

    And expression gets evaluated to produce a value.

  • This is a statement:

    x = -3
    if x > 0:
        print("x is positive")
    else:
        print("x is not positive")
    
    x is not positive
    

    A statement is not evaluated. it does not produce a value. It is executed according to the semantics of, in this case, the if statement.

  • Note that Python allows for functions that return any number of values. To return more than one value, return a tuple. An example of a function which returns two values is math.modf which returns the fractional and integer parts of its argument.

    import math
    
    frac, whole = math.modf(23.45)
    
    print(f"The whole part is {whole}\nThe fractional part is {frac}")
    
    The whole part is 23.0
    The fractional part is 0.4499999999999993
    
  • You can write a function which returns multiple values like this.

    def three_harmonics(n):
        return 1 / n, 2 / n, 3 / n
    
    
    three_harmonics(5)
    
    (0.2, 0.4, 0.6)
    
  • To return no value, depending on your needs you might return the empty tuple () or simply not return anything in which case the function returns None
  • In Lisp, everything is an expression, even constructs that you might think of as statements, such as if and setf. This means that we can write

    (setf v (if (> 3 4)
                (+ 5 6)
                (+ 7 8)))
    

    Here the if returns the value of its else case, namely 15. Setf stores that value in the variable v and then returns it.

  • In Python we might write

    def announce_bananas(n):
        if n > 0:
            print("We have some bananas today.")
        else:
            print("We have no bananas today.")
    

    If it were something more complex you might write

    def announce_bananas(n):
        if n > 0:
            amount = "some"
        else:
            amount = "no"
        print(f"We have {amount} bananas today.")
    
  • In Lisp you would simply write

    (defun announce-bananas (n)
      (format t
              "We have ~a bananas today."
              (if (> n 0)
                  "some"
                  "no")))
    
    (announce-bananas 23)
    (announce-bananas 0)
    
  • In Lisp a function can return more, or fewer, than one value. An example of a function that returns two values is floor

    * (floor 7 4)
    1
    3
    *
    
  • To capture multiple values, use multiple-value-bind

    (multiple-value-bind (times remainder)
        (floor 7 4)
      (format nil "Four goes into seven ~r time~p with ~r left over"
              times times remainder))
    

    In passing, note the pluralisation directive ~p which pluralises the previous word if the corresponding argument requires that. It's not very clever though.

    (format nil "mouse~p" 2) => "mouses"
    

    Also note the ~r directive which prints a number as a word or even Roman numerals.

    (format nil "The year ~@r is ~r." 2022 2022)
    "The year MMXXII is two thousand twenty-two."
    

    See also A Few FORMAT Recipes

  • In Lisp you can return more, or fewer, than one value using the values form. Here is an example of a function that returns two values.

    (defun polar (x y)
      (values (sqrt (+ (* x x) (* y y)))
              (atan y x)))
    

    You can return zero values with simply (values)

Control Flow in Python and Lisp

The Common Lisp Cookbook

Housekeeping

etc

Author: Breanndán Ó Nualláin <o@uva.nl>

Date: 2025-03-13 Thu 10:55