(defun name varlist &rest body)
The message will disappear as you start to type each new element but will reappear each time you enter a space. When you're entering the definition in the file, you might choose to break the definition across two lines after the parameter list. If you hit Return and then Tab, SLIME will automatically indent the second line appropriately, like this:[20]
(defun hello-world ()
(format t "hello, world"))
SLIME will also help match up the parentheses—as you type a closing parenthesis, it will flash the corresponding opening parenthesis. Or you can just type C-c C-q
to invoke the command slime-close-parens-at-point
, which will insert as many closing parentheses as necessary to match all the currently open parentheses.
Now you can get this definition into your Lisp environment in several ways. The easiest is to type C-c C-c
with the cursor anywhere in or immediately after the DEFUN
form, which runs the command slime-compile-defun
, which in turn sends the definition to Lisp to be evaluated and compiled. To make sure this is working, you can make some change to hello-world
, recompile it, and then go back to the REPL, using C-c C-z
or C-x b
, and call it again. For instance, you could make it a bit more grammatical.
(defun hello-world ()
(format t "Hello, world!"))
Next, recompile with C-c C-c
and then type C-c C-z
to switch to the REPL to try the new version.
CL-USER> (hello-world)
Hello, world!
NIL
You'll also probably want to save the file you've been working on; in the hello.lisp
buffer, type C-x C-s
to invoke the Emacs command save-buffer
.
Now to try reloading this function from the source file, you'll need to quit Lisp and restart. To quit you can use a SLIME shortcut: at the REPL, type a comma. At the bottom of the Emacs window, you will be prompted for a command. Type quit
(or sayoonara
), and then hit Enter. This will quit Lisp and close all the buffers created by SLIME such as the REPL buffer.[21] Now restart SLIME by typing M-x slime
.
Just for grins, you can try to invoke hello-world
.
CL-USER> (hello-world)
At that point SLIME will pop up a new buffer that starts with something that looks like this:
attempt to call `HELLO-WORLD' which is an undefined function.
[Condition of type UNDEFINED-FUNCTION]
Restarts:
0: [TRY-AGAIN] Try calling HELLO-WORLD again.
1: [RETURN-VALUE] Return a value instead of calling HELLO-WORLD.
2: [USE-VALUE] Try calling a function other than HELLO-WORLD.
3: [STORE-VALUE] Setf the symbol-function of HELLO-WORLD and call it again.
4: [ABORT] Abort handling SLIME request.
5: [ABORT] Abort entirely from this process.
Backtrace:
0: (SWANK::DEBUG-IN-EMACS #<UNDEFINED-FUNCTION @ #x716b082a>)
1: ((FLET SWANK:SWANK-DEBUGGER-HOOK SWANK::DEBUG-IT))
2: (SWANK:SWANK-DEBUGGER-HOOK #<UNDEFINED-FUNCTION @ #x716b082a> #<Function SWANK-DEBUGGER-HOOK>)
3: (ERROR #<UNDEFINED-FUNCTION @ #x716b082a>)
4: (EVAL (HELLO-WORLD))
5: (SWANK::EVAL-REGION "(hello-world)
" T)
Blammo! What happened? Well, you tried to invoke a function that doesn't exist. But despite the burst of output, Lisp is actually handling this situation gracefully. Unlike Java or Python, Common Lisp doesn't just bail—throwing an exception and unwinding the stack. And it definitely doesn't dump core just because you tried to invoke a missing function. Instead Lisp drops you into the debugger.
While you're in the debugger you still have full access to Lisp, so you can evaluate expressions to examine the state of our program and maybe even fix things. For now don't worry about that; just type q
to exit the debugger and get back to the REPL. The debugger buffer will go away, and the REPL will show this:
CL-USER> (hello-world)
; Evaluation aborted
CL-USER>
There's obviously more that can be done from within the debugger than just abort—we'll see, for instance, in Chapter 19 how the debugger integrates with the error handling system. For now, however, the important thing to know is that you can always get out of it, and back to the REPL, by typing q
.
Back at the REPL you can try again. Things blew up because Lisp didn't know the definition of hello-world
. So you need to let Lisp know about the definition we saved in the file hello.lisp
. You have several ways you could do this. You could switch back to the buffer containing the file (type C-x b
and then enter hello.lisp
when prompted) and recompile the definition as you did before with C-c C-c
. Or you can load the whole file, which would be a more convenient approach if the file contained a bunch of definitions, using the LOAD
function at the REPL like this:
CL-USER> (load "hello.lisp")
; Loading /home/peter/my-lisp-programs/hello.lisp
T
The T
means everything loaded correctly.[22] Loading a file with LOAD
is essentially equivalent to typing each of the expressions in the file at the REPL in the order they appear in the file, so after the call to LOAD
, hello-world
should be defined:
CL-USER> (hello-world)
Hello, world!
NIL
Another way to load a file's worth of definitions is to compile the file first with COMPILE-FILE
and then LOAD
the resulting compiled file, called a FASL file, which is short for fast-load file. COMPILE-FILE
returns the name of the FASL file, so we can compile and load from the REPL like this:
CL-USER> (load (compile-file "hello.lisp"))
;;; Compiling file hello.lisp
;;; Writing fasl file hello.fasl
;;; Fasl write complete
; Fast loading /home/peter/my-lisp-programs/hello.fasl
T
SLIME also provides support for loading and compiling files without using the REPL. When you're in a source code buffer, you can use C-c C-l
to load the file with slime-load-file
. Emacs will prompt for the name of a file to load with the name of the current file already filled in; you can just hit Enter. Or you can type C-c C-k
to compile and load the file represented by the current buffer. In some Common Lisp implementations, compiling code this way will make it quite a bit faster; in others, it won't, typically because they always compile everything.
This should be enough to give you a flavor of how Lisp programming works. Of course I haven't covered all the tricks and techniques yet, but you've seen the essential elements—interacting with the REPL trying things out, loading and testing new code, tweaking and debugging. Serious Lisp hackers often keep a Lisp image running for days on end, adding, redefining, and testing bits of their program incrementally.
Also, even when the Lisp app is deployed, there's often still a way to get to a REPL. You'll see in Chapter 26 how you can use the REPL and SLIME to interact with the Lisp that's running a Web server at the same time as it's serving up Web pages. It's even possible to use SLIME to connect to a Lisp running on a different machine, allowing you—for instance—to debug a remote server just like a local one.
20
You could also have entered the definition as two lines at the REPL, as the REPL reads whole expressions, not lines.
22
If for some reason the LOAD
doesn't go cleanly, you'll get another error and drop back into the debugger. If this happens, the most likely reason is that Lisp can't find the file, probably because its idea of the current working directory isn't the same as where the file is located. In that case, you can quit the debugger by typing q
and then use the SLIME shortcut cd
to change Lisp's idea of the current directory—type a comma and then cd
when prompted for a command and then the name of the directory where hello.lisp
was saved.