Jim Cheung

Practical Common Lisp

Chapter 1 Introduction: Why Lisp?

(no notes)

Chapter 2 Lather, Rinse, Repeat: A Tour of the REPL

install lispbox

slime keys:

(also read slime documentation)

Chapter 3 Practical: A Simple Database

(no notes)

Chapter 4 Syntax and Semantics

(no notes were taken, the whole chapter is important)

Chapter 5 Functions

Defining New Functions

(defun name (parameter*)
  "Optional documentation string."
  body-form*)

Any symbol can be used as a function name.

The last expression is returned as the value of the function.

Optional Parameters

(defun foo (a b &optional c d) (list a b c d))
;; with default value provided
(defun foo (a &optional (b 10)) (list a b))
;; and with supplied predicate
(defun foo (a b &optional (c 3 c-supplied-p))
;; the default-value expression can refer to parameters that occur earlier in the parameter list
(defun make-rectangle (width &optional (height width)) ...)

Rest Parameters

;; numbers is a list
(defun + (&rest numbers) ...)

Keyword Parameters

keywords are names that start with a colon and that they're automatically defined as self-evaluating constants.

(defun foo (&key a b c) (list a b c))
;; with default value and supplied-p
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
;; specify the parameter to be different from the name of the actual parameter
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
;; call with
(foo :apple 10 :box 20 :charlie 30) ==> (10 20 30 T)

Mixing Different Parameter Types

they must be declared in the order I've discussed them: first the names of the required parameters, then the optional parameters, then the rest parameter, and finally the keyword parameters.

either &optional or &rest parameters combined with &key parameters, can lead to somewhat surprising behavior.

Function Return Values

use the RETURN-FROM special operator to immediately return any value from the function.

(defun foo (n)
  (dotimes (i 10)
    (dotimes (j 10)
      (when (> (* i j) n)
        (return-from foo (list i j))))))

Functions As Data, a.k.a. Higher-Order Functions

In Lisp, functions are just another kind of object. The special operator FUNCTION provides the mechanism for getting at a function object.

CL-USER> (defun foo (x) (* 2 x))
FOO
CL-USER> (function foo)
#<Interpreted Function FOO>
;; The syntax #' is syntactic sugar for FUNCTION
CL-USER> #'foo
#<Interpreted Function FOO>

two functions for invoking a function through a function object: FUNCALL and APPLY

FUNCALL is the one to use when you know the number of arguments you're going to pass to the function at the time you write the code.

(foo 1 2 3) === (funcall #'foo 1 2 3)

FUNCALL, however, doesn't do you any good when the argument list is known only at runtime.

That's where APPLY comes in. the 2nd parameter is a list, it then applies the function to the values in the list.

APPLY can also accept "loose" arguments as long as the last argument is a list.

APPLY doesn't care about whether the function being applied takes &optional, &rest, or &key arguments, the final list must be a legal argument list for the function with enough arguments for all the required parameters and only appropriate keyword parameters.

Anonymous Functions

(lambda (parameters) body)

One way to think of LAMBDA expressions is as a special kind of function name where the name itself directly describes what the function does. This explains why you can use a LAMBDA expression in the place of a function name with #'.

(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5
;; You can even use a LAMBDA expression as the "name" of a function in a function call expression.
((lambda (x y) (+ x y)) 2 3) ==> 5

LAMBDA expressions can be used anywhere a normal function name can be.

Chapter 6 Variables

Common Lisp supports two kinds of variables: lexical and dynamic. These two types correspond roughly to "local" and "global" variables in other languages.

Common Lisp is dynamically typed–type errors are detected dynamically. On the other hand, Common Lisp is a strongly typed language in the sense that all type errors will be detected.

;; this function defines three variables--x, y, and z--to hold its arguments. 
(defun foo (x y z) (+ x y z))
;; binds the three variables x, y, and z with initial values 10, 20, and NIL
(let ((x 10) (y 20) z)
    ...)

Like function parameters, variables introduced with LET are rebound each time the LET is entered.

The scope of function parameters and LET variables is delimited by the form that introduces the variable. This form--the function definition or the LET–is called the binding form.

(defun foo (x)
  (format t "Parameter: ~a~%" x)      ; |<------ x is argument 
  (let ((x 2))                        ; |
    (format t "Outer LET: ~a~%" x)    ; | |<---- x is 2
    (let ((x 3))                      ; | |
      (format t "Inner LET: ~a~%" x)) ; | | |<-- x is 3
    (format t "Outer LET: ~a~%" x))   ; | |
  (format t "Parameter: ~a~%" x))     ; |

Lexical Variables and Closures

By default all binding forms in Common Lisp introduce lexically scoped variables. Lexically scoped variables can be referred to only by code that's textually within the binding form.

;; The binding of count created when the flow of control entered the LET form will stick around for as long as needed
(let ((count 0)) #'(lambda () (setf count (1+ count))))

The anonymous function is called a closure because it "closes over" the binding created by the LET.

The key thing to understand about closures is that it's the binding, not the value of the variable, that's captured.

Dynamic, a.k.a. Special, Variables

Common Lisp provides two ways to create global variables: DEFVAR and DEFPARAMETER.

The difference between the two forms is that DEFPARAMETER always assigns the initial value to the named variable while DEFVAR does so only if the variable is undefined.

global variables are conventionally named with names that start and end with *

if you want to temporarily redefine *standard-output*, the way to do it is simply to rebind it, say, with a LET.

(let ((*standard-output* *some-other-stream*))
  (stuff))

The name of every variable defined with DEFVAR and DEFPARAMETER is automatically declared globally special.

This means whenever you use such a name in a binding form–in a LET or as a function parameter or any other construct that creates a new variable binding–the binding that's created will be a dynamic binding. This is why the *naming* *convention* is so important.

Constants

All constants are global and are defined with DEFCONSTANT.

many Lisp programmers follow a naming convention of using names starting and ending with + for constants.

Assignment

To assign a new value to a binding, you use the SETF macro

(setf place value)
;; assign to multiple places in sequence
(setf x 1 y 2)

SETF returns the newly assigned value.

Other Ways to Modify Places

(incf x)    === (setf x (+ x 1))
(decf x)    === (setf x (- x 1))
(incf x 10) === (setf x (+ x 10))

(rotatef a b) ;; swaps the values of the two variables and returns NIL.
(shiftf a b 10) ;; shifts them to the left, return the original value of the first argument

Chapter 7 Macros: Standard Control Constructs

Chapter 8 Macros: Defining Your Own

Chapter 9 Practical: Building a Unit Test Framework

Chapter 10 Numbers, Characters, and Strings

Chapter 11 Collections

Chapter 12 They Called It LISP for a Reason: List Processing

Chapter 13 Beyond Lists: Other Uses for Cons Cells

Chapter 14 Files and File I/O

Chapter 15 Practical: A Portable Pathname Library

Chapter 16 Object Reorientation: Generic Functions

Chapter 17 Object Reorientation: Classes

Chapter 18 A Few FORMAT Recipes

Chapter 19 Beyond Exception Handling: Conditions and Restarts

Chapter 20 The Special Operators

Chapter 21 Programming in the Large: Packages and Symbols

Chapter 22 LOOP for Black Belts

Chapter 23 Practical: A Spam Filter

Chapter 24 Practical: Parsing Binary Files

Chapter 25 Practical: An ID3 Parser

Chapter 26 Practical: Web Programming with AllegroServe

Chapter 27 Practical: An MP3 Database

Chapter 28 Practical: A Shoutcast Server

Chapter 29 Practical: An MP3 Browser

Chapter 30 Practical: An HTML Generation Library, the Interpreter

Chapter 31 Practical: An HTML Generation Library, the Compiler

Chapter 32 Conclusion: What's Next?