Lisp( programming language)
Lisp is the second high-level programming language, and more importantly introduced a lot of great concepts that are still used in a lot of scientific computing applications today. Although Lisp certainly classifies as high-level, the differences between a Lisp-like language and the hundreds of other programming languages can be felt across the board. Curiously, Lisp has been associated with machine-learning since before R or Python were even born, but is incredibly rare to come by these days in any discipline.
So with that in mind, why aren’t we using Lisp for Data Science? This is not to say that the amazing Python shouldn’t be the top programming language for scientific computing, because it certainly deserves that spot, but what benefits could adding a language like Common Lisp have to your Data Science arsenal?
Lisp was invented in 1958, which makes it the second-oldest computer programming language. It has spawned several modern derivatives, including Common Lisp, Emacs Lisp (Elisp), Clojure, Racket, Scheme, Fennel, and GNU Guile.
People who love thinking about the design of programming languages often love Lisp because of how its syntax and data share the same structure: Lisp code is essentially a list of lists, and its name is an acronym for LISt Processing. People who love thinking about the aesthetics of programming languages often hate Lisp because of its frequent use of parentheses for scoping; in fact, it’s a common joke that Lisp stands for Lots of Irritating Superfluous Parentheses.
Whether you love or hate its design philosophies, Lisp is an interesting glimpse at the past and, thanks to Clojure and Guile, into the future. You might be surprised how much Lisp code there is lurking within big codebases in any given industry, so it’s a good idea to have at least a passing familiarity with the language.
Install Lisp
There are many implementations of Lisp. Popular open source versions include SBCL, GNU Lisp, and GNU Common Lisp (GCL). You can install any of these with your distribution’s package manager, but for this article I use clisp
.
On Fedora Linux:
$ sudo dn f install c lisp
On Debian:
$ sudo apt install c lisp
For macOS, you can use Mac Ports or Homebrew:
$ sudo port install c lisp
For Windows, you can either use c lisp
on Cygwin or download a GCL binary from gnu.org/software/gcl.
Even though I’m using the clisp
command, most of the principles in this article apply to any Lisp. Should you choose to use a different Lisp implementation, the command to run Lisp code is different from what I use in this article (gc l
or sbc l
instead of c lisp
, for example) but everything else is the same.
List processing
The basic unit of Lisp source code is an expression, which is written as a list. For instance, this is a list of an operator (+
) and two integers (1
and 2
):
(+ 1 2)
It’s also a Lisp expression, using a symbol (+
) that evaluates to a function (addition) and two arguments (1
and 2
). You can run this expression and others in an interactive Common Lisp environment called REPL (read-eval-print loop). If you're familiar with Python's IDLE, Lisp's REPL should feel somewhat familiar to you.
To launch a REPL, launch Common Lisp:
$ c lisp
[1]>
At the REPL prompt, type a few expressions:
[1]> (+ 1 2)
3
[2]> (- 1 2)
-1
[3]> (- 2 1)
1
[4]> (+ 2 3 4)
9
Functions
Now that you know the basic structure of a Lisp expression, you can utilize Lisp functions in useful ways. The print
function takes any argument you provide and displays it on your terminal, while the p print
function "pretty" prints it. There are other variations on the print function, but p print
is nice in REPL:
[1]> (p print "hello world")
"hello world"
[2]>
You can create your own functions with defun
. The defun
function requires a name for your function and any parameters you want your function to accept:
[1]> (defun myprinter (s) (pprint s))
MYPRINTER
[2]> (myprinter "hello world")
"hello world"
[3]>
Variables
You can create variables in Lisp with set f
:
[1]> (set f foo "hello world")
"hello world"
[2]> (p print foo)
"hello world"
[3]>
You can nest expressions within expressions in a kind of pipeline. For instance, you can pretty print the contents of your variable after invoking the string-upcase
function to convert its characters to uppercase:
[3]> (pprint (string-upcase foo))
"HELLO WORLD"
[4]>
Lisp is dynamically typed in the sense that you don’t have to declare variable types when setting them. Lisp treats integers as integers by default:
[1]> (setf foo 2)
[2]> (setf bar 3)
[3]> (+ foo bar)
5
If you intend for an integer to be interpreted as a string, you can quote it:
[4]> (setf foo "2")
"2"
[5]> (setf bar "3")
"3"
[6]> (+ foo bar)
*** - +: "2" is not a number
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead.
ABORT :R2 Abort main loop
Break 1 [7]>
In this sample REPL session, both foo
and bar
are set to quoted numbers, so Lisp interprets them as strings. Math operators can't be used on strings, so REPL drops into a debugger mode. To get out of the debugger, press Ctrl + D on your keyboard.
You can do some introspection on objects using the type p
function, which tests for a specific data type. The tokens T
and NIL
represent True and False, respectively.
[4]> (typep foo 'string)
NIL
[5]> (typep foo 'integer)
T
The single quote ('
) before string
and integer
prevents Lisp from (incorrectly) evaluating those keywords as variables:
[6]> (typep foo string)
*** - SYSTEM::READ-EVAL-PRINT: variable STRING has no value
[...]
It’s a shorthand way to protect the terms, normally done with the quote
function:
[7]> (typep foo (quote string))
NIL
[5]> (typep foo (quote integer))
T
Lists
Unsurprisingly, you can also create lists in Lisp:
[1]> (setf foo (list "hello" "world"))
("hello" "world")
Lists can be indexed with the nth
function:
[2]> (nth 0 foo)
"hello"
[3]> (pprint (string-capitalize (nth 1 foo)))
"World"
Exiting REPL
To end a REPL session, press Ctrl+D on your keyboard, or use the quit
keyword in Lisp:
[99]> (quit)
$
Scripting
Lisp can be compiled or used as an interpreted scripting language. The latter is probably the easiest option when you’re starting, especially if you’re already familiar with Python or shell scripting.
Here’s a simple dice roller script written in GNU Common Lisp: