In a previous post we have talked about creating lists in Lisp using the quote special operator.
Now we will introduce the list function that allows us to build mutable lists in the canonical Lisp way.
By the end of this post you will also understand why building a list with the list function is so much different from doing it using the quote special operator.
Creating a list with the list function
Using the list function to is straightforward: just add it as the first element of a list (a couple of parenthesis with elements inside) followed by the elements of the list you want inside it.
* (list 1 2 3)
(1 2 3)
As you can see, the Lisp REPL will return us the newly created list with the elements inside.
At this point you may be wondering why you should use the list function rather than the quote special operator.
list function VS quote operator
At first sight, the list function is not producing nothing different than before.
* (quote (1 2 3))
(1 2 3)
* (list 1 2 3)
(1 2 3)
Given a sequence of numbers, it returns a list containing those numbers.
The only noticeable difference is the the missing parenthesis around the numbers, but the result looks the same as with quote.
What is happening here is clear: using the list function, every parameter of the function is being evaluated before it is added to the list.
The quote special operator, instead, is creating the list as is without evaluating its elements.
This behaviour is even more explicit if we try creating a list of characters.
* '(a b c)
(A B C)
* (list a b c)
debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {1000510083}>:
The variable A is unbound.
...
Using the quote special operator, a list is created and its elements are converted to symbols.1
Intead, using the list function, the Lisp compiler tried to evaluate every element starting from a, resulting in an error: an a variable does not exists in this scope.
You can always create the same quoted list using the list function quoting each element.2
* (list 'a 'b 'c)
(A B C)
When to use quote or list?
Creating a list is different from quoting a list, so you can not use them interchangeably.
You should use the list function when you want the list elements being evaluated before the creation of the list.
Moreover, creating a new list means creating new elements for it.
Instead, with the quote operator you have a list of symbols, and this is something completely different from a plain old list.
Don’t worry, we will talk about symbols in another post. The conversion to symbols is also the reason behind why you should not modify a quoted list: symbols could be shared by the compiler between different lists… ↩
This is exactly what the quote special operator does: it quotes each element in the given list. ↩
You may have heard that Lisp code is all about parentheses - and it is true.
But do you really know why Lisp code is made by so many parentheses?
Well, the answer is quite simple: Lisp code is just a long sequence of lists and the parentheses are the way these lists are created.
Lisp stands for LISt Processor so it should not be a surprise if its code is full of lists.1
Parentheses are lists’ delimiters, so everything between them is part of a list.
An empty list
Creating a list is simple - just write a single quote character followed by a couple of parentheses.2
This is an empty list:
'()
Simple lists
Creating a list containing items is straightforward.
Just add the elements you want between the parentheses, separating them with a whitespace.
'(1)
'(1 2)
'(one two three)
'(a list with mixed content 42 "and a string")
Whitespaces inside a list
As we said, a single whitespace is required to separate list’s items, but there is no limit on the number of whitespace you can add between them.
Adding more whitespaces does not have any effect on the list.
So you can rewrite the last list in this way and it will be the same as before.
'(a
list
with mixed content
42
"and a string")
A list inside a list
If you were wondering if you can put a list inside another list, the answer is definitely yes.
'(a list (with another list))
And yes, you can go as deep and complex as you want.
'(a (list) (with (another) list))
Code VS lists
We said that a single quote character is required before the list in order to create it.
Indeed, if you try to write one of such lists in the Lisp REPL, it will return you the list itself.
* '(one two three)
(ONE TWO THREE)
And what happens when you try to remove the quote character? Fasten your seatbelts because the output is quite long.
* (one two three)
; in: ONE TWO
; (ONE TWO THREE)
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::ONE
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::THREE
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::TWO
;
; compilation unit finished
; Undefined function:
; ONE
; Undefined variables:
; THREE TWO
; caught 2 WARNING conditions
; caught 1 STYLE-WARNING condition
debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {1000510083}>:
The variable TWO is unbound.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE ] Retry using TWO.
1: [USE-VALUE ] Use specified value.
2: [STORE-VALUE] Set specified value and use it.
3: [ABORT ] Exit debugger, returning to top level.
((LAMBDA ()))
source: (ONE TWO THREE)
0] 3
As you can see from the logs, the Lisp REPL tried to evaluate the list but it failed on the first element - one.
It expected to find a function there, but it found the one symbol.3
This is due to the way Lisp works.
If a list is not preceded by a single quote character, it will be not evaluated as data, but as code.
Indeed, if you try to add a single quote before a list containing a sum, it will not be evaluated.
* (+ 1 2)
3
* '(+ 1 2)
(+ 1 2)
So, the difference between code and data is just that the former is evaluated (“executed”) by the Lisp REPL (or compiler) when read.
The latter, instead, is parsed and kept as-is.
The quote operator
Until now we have created lists using the single quote character, but it is just a shortcut for the Lisp special operator quote.4
* (quote (+ 1 2))
(+ 1 2)
Quoting means escaping and also avoiding the evaluation of the quoted expression.
You can use the quote special operator to quote almost anything, not only lists.
* (quote a)
a
* (quote 1)
1
* (quote "hello world")
"hello world"
The semantics of the quote operator is “get an object and return it as is, without evaluating it” - and this is the exact definition of the word “quotation”.
You should not modify a quoted object since results are unpredictable.
You can have a language specialised in list processing without having to write its code as lists. But having your code written as the data it is able to process enable some powerful features. One of this feature is the ability for the code to change itself - the macro system. We will talk about this in another post. ↩
The single quote character is also called apostrophe, acute accent or just quote. ↩
Yes, we will talk about symbols in another post. ↩
Special operators, special forms… Lisp has a lot of special things that need to be known. We will see them one at a time to avoid overloading our brains. ↩
While learning the basics of the SBCL REPL you may have noticed its strange behaviour when it comes to the output of some instructions.
Making a sum will produce a single output with the result of the operation.
* (+ 1 5)
6
The same will happen when calculating the square root of a number.
* (sqrt 4)
2.0
But if we try to print a string using the print function, the result will be quite strange - an empty line followed by the same string printed twice.
* (print "twice")
"twice"
"twice"
In order to understand why a blank line is added to the output, we can try using another printing function called prin1.
Its output is equal to print except for the missing blank line.
* (prin1 "twice")
"twice"
"twice"
As you can see from the functions’ documentation, the only difference between them is exactly this empy line (a new line actually) printed before the output.
The print function will add it to the output, while prin1 not.
But we have not yet discovered why the string is printed twice.
The mystery is quite simple to solve, because the first string printed by the function is its output in the standard out.
The second string is the value returned by the function and printed by the REPL - in a non-interactive program you will never see this behaviour.
If you need another prove of this behaviour you could try to assign the output of the print function to a variable (str) and then transforming it in uppercase.
Here the behaviour is clear.
The blank line is added by print as the first string (the case has been kept).
The last line is the uppercase version of the string, but we have not explicitly printed it.
So it could have been printed only by the REPL itself - and this is the proof we were looking for.
Steel Bank Common Lisp SBCL is a Common Lisp implementation derived from the Carniege Mellon University Common Lisp (CMUCL). The “steel” and the “bank” in the name are references to the businesses of its founders (Andrew Carnegie and Andrew Mellon) so the relationship between them is clear. SBCL is very popular and you can get it running on your machine quite easily.
Installing on Linux
Almost all Linux distributions include a sbcl package.
Fedora -> sudo dnf install sbcl (not always updated to the latest version)
You can download a prebuild binary or sources for any platform from the dedicated page on the SBCL website.
Also a ready-to-use docker image exists, and even if it is not an official image you can trust it because it is from a well known Lisper.
Running
Executing sbcl from a command line will show you an initial information message and the prompt - the last line starting with *.
$ sbcl
This is SBCL 2.0.1, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
*
In this way we have started the SBCL interactive REPL1 so we can type instructions and execute them pressing Enter. The result of the computation will be shown below.
* (+ 1 2)
3
Quitting
Now you may be wondering how to quit the REPL, and so you may have pressed the ubiquitous CTRL-C. It doesn’t work and this weird error is shown.
* ^C
debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {1000510083}>:
Interactive interrupt at #x7F41875058D7.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Return from SB-UNIX:SIGINT.
1: [ABORT ] Exit debugger, returning to top level.
("bogus stack frame")
Sometimes stopping an application is not as obvious as we might think. And it looks like this is another chapter from the Art of Quitting book. To quit from the SBCL REPL you just need to type (quit) followed by Enter.
Read–eval–print loop. The first REPL was truly invented in 1964 by L. Peter Deutsch and Edmund Berkeley while implementing Lisp on the PDP-1. Peter Deutsch was 17. ↩
Your journey into the Lisp land starts with a choice - and it is not the choice between starting or not.
The choice you have to make is about the Lisp dialect you want to learn.
Since Lisp is a family of programming languages you can’t actually write Lisp code.
Therefore you need to use a Lisp dialect such as Scheme, Common Lisp, Clojure or Racket.
The complete list is much longer but these four are the most popular and active nowadays.
Suppose you choose Common Lisp.
Do you think you could just download it and start writing code?
Unfortunately it is not so easy and another choice is required.
Since Common Lisp is a specified language1, this time you need to pick an implementation among the many available implementations.
SBCL (Steel Bank Common Lisp), Allegro Common Lisp and LispWorks are just three examples from the existing dozens.
Every specification-compliant Common Lisp implementation behaves similarly to the others.
So how to choose the right one for you?
Despite being all similar, each implementation is different from the others.
In fact, some implementations could support some operating systems and particular architectures (e.g. ARM) while others do not.
Another key difference is the license. For example, SBCL is free software while Allegro Common Lisp has a commercial license with a freeware option.
Moreover, some implementations provide useful extentions and advanced features for specific use cases. For example, LispWorks which is shipped with a Java interface2 and a multiplatform GUI Toolkit.
When using Scheme or Clojure you have a similar situation.
With Clojure you can pick Clojure itself, running on the JVM, Clojure CLR, running on the .NET platform or many other implementations.
If you want, you can even use it in your browser using ClojureScript.
The only exception in the four Lisps listed at the beginning of this post is Racket, and in future posts we will see why.
So, where to start?
A wise choice is to start with SBCL that is free and available for almost every platform3.
After the installation you run it and start playing with the REPL (read-eval-print-loop) from the command line.
They have also created a mirror on Github so you can follow the releases using Github notifications.
If you are not satisfied, you can always pick another implementation and use it - your code will continue to work in the same way as before.
All these options can be a bit confusing, but after learning the relationships between Lisp dialects and the implementations, everything will be clear and you will be happy to have so many choices.