CL-USER> 10
10
The first 10
is the one you typed. The Lisp reader, the R in REPL, reads the text "10" and creates a Lisp object representing the number 10. This object is a self-evaluating object, which means that when given to the evaluator, the E in REPL, it evaluates to itself. This value is then given to the printer, which prints the 10
on the line by itself. While that may seem like a lot of work just to get back to where you started, things get a bit more interesting when you give Lisp something meatier to chew on. For instance, you can type (+ 2 3)
at the Lisp prompt.
CL-USER> (+ 2 3)
5
Anything in parentheses is a list, in this case a list of three elements, the symbol +
, and the numbers 2 and 3. Lisp, in general, evaluates lists by treating the first element as the name of a function and the rest of the elements as expressions to be evaluated to yield the arguments to the function. In this case, the symbol +
names a function that performs addition. 2 and 3 evaluate to themselves and are then passed to the addition function, which returns 5. The value 5 is passed to the printer, which prints it. Lisp can evaluate a list expression in other ways, but we needn't get into them right away. First we have to write. . .
"Hello, World," Lisp Style
No programming book is complete without a "hello, world"[16] program. As it turns out, it's trivially easy to get the REPL to print "hello, world."
CL-USER> "hello, world"
"hello, world"
This works because strings, like numbers, have a literal syntax that's understood by the Lisp reader and are self-evaluating objects: Lisp reads the double-quoted string and instantiates a string object in memory that, when evaluated, evaluates to itself and is then printed in the same literal syntax. The quotation marks aren't part of the string object in memory—they're just the syntax that tells the reader to read a string. The printer puts them back on when it prints the string because it tries to print objects in the same syntax the reader understands.
However, this may not really qualify as a "hello, world" program. It's more like the "hello, world" value.
You can take a step toward a real program by writing some code that as a side effect prints the string "hello, world" to standard output. Common Lisp provides a couple ways to emit output, but the most flexible is the FORMAT
function. FORMAT
takes a variable number of arguments, but the only two required arguments are the place to send the output and a string. You'll see in the next chapter how the string can contain embedded directives that allow you to interpolate subsequent arguments into the string, à la printf
or Python's string-%
. As long as the string doesn't contain an ~
, it will be emitted as-is. If you pass t
as its first argument, it sends its output to standard output. So a FORMAT
expression that will print "hello, world" looks like this:[17]
CL-USER> (format t "hello, world")
hello, world
NIL
One thing to note about the result of the FORMAT
expression is the NIL
on the line after the "hello, world" output. That NIL
is the result of evaluating the FORMAT
expression, printed by the REPL. (NIL
is Lisp's version of false and/or null. More on that in Chapter 4.) Unlike the other expressions we've seen so far, a FORMAT
expression is more interesting for its side effect—printing to standard output in this case—than for its return value. But every expression in Lisp evaluates to some result.[18]
However, it's still arguable whether you've yet written a true "program." But you're getting there. And you're seeing the bottom-up style of programming supported by the REPL: you can experiment with different approaches and build a solution from parts you've already tested. Now that you have a simple expression that does what you want, you just need to package it in a function. Functions are one of the basic program building blocks in Lisp and can be defined with a DEFUN
expression such as this:
CL-USER> (defun hello-world () (format t "hello, world"))
HELLO-WORLD
The hello-world
after the DEFUN
is the name of the function. In Chapter 4 we'll look at exactly what characters can be used in a name, but for now suffice it to say that lots of characters, such as -
, that are illegal in names in other languages are legal in Common Lisp. It's standard Lisp style—not to mention more in line with normal English typography—to form compound names with hyphens, such as hello-world
, rather than with underscores, as in hello_world
, or with inner caps such as helloWorld
. The ()
s after the name delimit the parameter list, which is empty in this case because the function takes no arguments. The rest is the body of the function.
At one level, this expression, like all the others you've seen, is just another expression to be read, evaluated, and printed by the REPL. The return value in this case is the name of the function you just defined.[19] But like the FORMAT
expression, this expression is more interesting for the side effects it has than for its return value. Unlike the FORMAT
expression, however, the side effects are invisible: when this expression is evaluated, a new function that takes no arguments and with the body (format t "hello, world")
is created and given the name HELLO-WORLD
.
Once you've defined the function, you can call it like this:
CL-USER> (hello-world)
hello, world
NIL
You can see that the output is just the same as when you evaluated the FORMAT
expression directly, including the NIL
value printed by the REPL. Functions in Common Lisp automatically return the value of the last expression evaluated.
Saving Your Work
You could argue that this is a complete "hello, world" program of sorts. However, it still has a problem. If you exit Lisp and restart, the function definition will be gone. Having written such a fine function, you'll want to save your work.
Easy enough. You just need to create a file in which to save the definition. In Emacs you can create a new file by typing C-x C-f
and then, when Emacs prompts you, entering the name of the file you want to create. It doesn't matter particularly where you put the file. It's customary to name Common Lisp source files with a .lisp
extension, though some folks use .cl
instead.
Once you've created the file, you can type the definition you previously entered at the REPL. Some things to note are that after you type the opening parenthesis and the word DEFUN
, at the bottom of the Emacs window, SLIME will tell you the arguments expected. The exact form will depend somewhat on what Common Lisp implementation you're using, but it'll probably look something like this:
16
The venerable "hello, world" predates even the classic Kernighan and Ritchie C book that played a big role in its popularization. The original "hello, world" seems to have come from Brian Kernighan's "A Tutorial Introduction to the Language B" that was part of the http://cm.bell-labs.com/cm/cs/who/dmr/bintro.html
.)
17
These are some other expressions that also print the string "hello, world":
(write-line "hello, world")
or this:
(print "hello, world")
18
Well, as you'll see when I discuss returning multiple values, it's technically possible to write expressions that evaluate to no value, but even such expressions are treated as returning NIL
when evaluated in a context that expects a value.