\( \newcommand\D{\mathrm{d}} \newcommand\E{\mathrm{e}} \newcommand\I{\mathrm{i}} \newcommand\bigOh{\mathcal{O}} \newcommand{\cat}[1]{\mathbf{#1}} \newcommand\curl{\vec{\nabla}\times} \newcommand{\CC}{\mathbb{C}} \newcommand{\NN}{\mathbb{N}} \newcommand{\QQ}{\mathbb{Q}} \newcommand{\RR}{\mathbb{R}} \newcommand{\ZZ}{\mathbb{Z}} \)
UP | HOME

On Lisp

Table of Contents

1. Overview

Lisp is the second-oldest high level language still used in production (Fortran is the oldest). Unlike other languages, we should think of Lisp at the level of abstract syntax trees, not statements or expressions. This allows us to transcend petty differences among languages.

Basically, if you want to create your own language but do not want to be dragged down by writing a parser, then you really want a Lisp. This is my own personal interest in Lisp: I want to have a language which permits me to do symbolic computation, and I don't care about syntax.

(See also further topics on Lisp.)

1.1. Basic Syntax and Semantics

All Lisp dialects share the same basic syntax, namely S-expressions. An S-expression is either an Atom or a List. Lisp atoms are literal values, or symbols. Lists are finite sequences of S-expressions surrounded by parentheses (S-1 S-2 ... S-n). Nested lists are permitted.

We interpret function calls using Polish notation. So instead of writing, as we would in C, 1 + 2 + 3 + 4 we have in Lisp (+ 1 2 3 4). This is evaluated by looking at the first element in the list, +, then check if it is a function or a macro. For functions, we evaluate the arguments passed to the function, then apply the function to the values (i.e., we "call by value"). Macros do not evaluate the arguments passed. Instead, macros produce a syntax tree, which is then evaluated. This permits us to extend Lisp with new language features.

The syntax for defining new functions or constants varies depending on dialect, but amount to another S-expression:

  • Scheme (define (my-function param-1 ... param-n) ...)
  • Common Lisp and Emacs Lisp (defun my-function (param-1 ... param-n) ...)
  • Clojure (defn my-function [param-1 ... param-n] ...)

That's basically it. We have an eval/apply loop, S-expressions encode data and code, and little else. Consequently, making a Lisp is one of the rites of passage in Computer Science.

2. Features

You get a language-factory for the cost of Latinized grammar (verb subject object). Once we accept this, we can extend the language with whatever feature we want. But there are a few other features worth mentioning.

Object-oriented programming in the C++/Java vein becomes (method object additional-param...). This lets CLOS to shine, giving us a far more sophisticated class system than C++ could ever produce, thanks to our being not-bogged-down-by-syntax. Well, Scheme and Common Lisp enjoy this benefit, Clojure is coupled to Java's object-oriented sytem.

Arguably any language you want to use could be transformed into this system, and any "killer feature" is either already in Lisp or easily implemented using macros. SICP shows this quite a few times over.

2.1. Multiple Dispatching

Common Lisp and Clojure offer multiple dispatching (Scheme gives you the pleasure of implementing it on your own). This permits, e.g., generic arithmetic: we can use * for matrix multiplication, scalar multiplication, and the underlying field's multiplication operator.

3. References

Last Updated 2022-04-22 Fri 08:25.