“Hello, World,” Lisp Style
No programming book is complete without a “hello, world”7 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:8
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.9
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.10 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.