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
- Lisp Road Map, C2 Wiki
- Steve Losh, A Road to Common Lisp. Blogpost August 27, 2018.
- How Lisp Became God's Own Programming Language. Two-Bit History, published 14 October 2018
- Christian Queinnec (tr. Kathleen Callaway),
Lisp in Small Pieces.
Cambridge University Press, 2003. - Peter Norvig,
(How to Write a (Lisp) Interpreter (in Python))