Reading — step 1 of 4
Learn
Common Lisp is a multi-paradigm language with deep traditions. These idioms separate Lispy code from "Java written in Lisp".
Naming conventions
*earmuffs*— special (dynamic) variables+constant+— true constants-name-— internal/private (no convention; prefix with%or--)predicate?— most use-psuffix:null,evenp,consp,string-equal-pmutator!—setf-style mutation:nreverse,nconc,delete(no!; CL has separate destructive names)from-to— converters:string-from-symbol,vector-from-list
Functional vs imperative
Lisp lets you do both. Idiomatic CL favors:
- Pure functions where possible
letoversetqfor naming intermediate valuesmapcar/reduceoverloopfor simple sequence opsloopfor complex iteration (it's the practical mini-language)
Don't fight nil
Lisp has nil doing triple duty:
- The empty list
() - The boolean false
- The "no value" returned from missing things
(if (gethash key table) ; nil = absent
(format t "found")
(format t "missing"))
(if (rest list) ; nil = empty
(process (rest list)))
(if user ; nil = absent
(greet user))
Idiomatic; embrace it.
Avoid setq at toplevel
Use defparameter (always sets) or defvar (only sets if undefined). Top-level setq works but is non-portable in some compilers.
Use sequence functions, not custom loops
Bad:
(let ((result nil))
(dolist (x lst)
(when (evenp x)
(push (* x 2) result)))
(nreverse result))
Good:
(mapcar (lambda (x) (* x 2)) (remove-if-not #'evenp lst))
Or with loop:
(loop for x in lst when (evenp x) collect (* x 2))
with- macros
For any resource that needs cleanup, write a with- macro:
(with-open-file (s "data.txt") ...)
(with-output-to-string (s) ...)
(with-lock-held (lock) ...)
(my-app:with-db-connection (conn) ...)
The pattern: bind the resource, run the body, ALWAYS clean up. Implement with unwind-protect.
&optional vs &key
(defun greet (name &optional (greeting "Hello"))
...)
(greet "Ada" "Hi")
(defun greet (name &key (greeting "Hello"))
...)
(greet "Ada" :greeting "Hi")
Keyword args are clearer when you have many — by name, any order. Optional args are positional. For >2 optional args, prefer keyword.
DEFAULTS
(let ((value (or maybe-value default)))
...)
or returns the first truthy value — Lisp's idiom for ?? (null coalesce). Watch for 0 and "" though — they're truthy in CL.
Don't write Java in Lisp
If you find yourself building elaborate class hierarchies for simple data, you've left Lisp. Use:
defstructfor plain records (faster than CLOS for simple cases)conscells / lists / hash tables for ad-hoc data- CLOS for polymorphism, multi-dispatch, runtime extensibility
Test in the REPL
The REPL is the IDE. Develop a function by:
- Define it
- Test in REPL with example data
- Refine
- Once working, save to file
Paul Graham, Peter Seibel, every working CL programmer lives here. SLIME (Emacs) and SLY are the standard editors.
Read other people's Lisp
Quicklisp's libraries are gold. Especially:
alexandria— utilities everyone usesiterate— alternative to loop, more macro-extensiblebordeaux-threads— portable threadscl-ppcre— regexusocket— socketshunchentoot/clack— web serverspostmodern— Postgres
Browse the source. Common Lisp culture values terse, expressive code over verbosity — it's a different aesthetic from Java/Python.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…