Revision 1.8

Robert Brown
François-René Rideau
In memoriam Dan Weinreb
This style guide contains many details that are initially hidden from view. They are marked by the triangle icon, which you see here on your left. Click it now. You should see "Hooray" appear below.

Hooray! Now you know you can expand points to get more details. Alternatively, there's an "expand all" at the top of this document.

Common Lisp is a powerful multiparadigm programming language. With great power comes great responsibility. This guide recommends formatting and stylistic choices designed to make your code easier for other people to understand.

This guide is not a Common Lisp tutorial. For basic information about the language, please consult Practical Common Lisp. For a language reference, please consult the Common Lisp HyperSpec. For more detailed style guidance, take a look at Peter Norvig and Kent Pitman's style guide.

Each guideline's level of importance is indicated by use of the following keywords and phrases, adapted from RFC 2119.
MUST

This word, or the terms "REQUIRED" or "SHALL", means that the guideline is an absolute requirement. You must ask permission to violate a MUST.

MUST NOT

This phrase, or the phrase "SHALL NOT", means that the guideline is an absolute prohibition. You must ask permission to violate a MUST NOT.

SHOULD

This word, or the adjective "RECOMMENDED", means that there may exist valid reasons in particular circumstances to ignore the demands of the guideline, but the full implications must be understood and carefully weighed before choosing a different course. You must ask forgiveness for violating a SHOULD.

SHOULD NOT

This phrase, or the phrase "NOT RECOMMENDED", means that there may exist valid reasons in particular circumstances to ignore the prohibitions of this guideline, but the full implications should be understood and carefully weighed before choosing a different course. You must ask forgiveness for violating a SHOULD NOT.

MAY

This word, or the adjective "OPTIONAL", means that an item is truly optional.

Unlike RFCs, we don't capitalize every instance of one of the above keywords when it is used.

There are cases where transgression of some of these rules is useful or even necessary. In some cases, you must seek permission or obtain forgiveness from the proper people.

Permission comes from the OWNERS of your project.

Forgiveness is requested in a comment near the point of guideline violation, and is granted by your code reviewer. The original comment should be signed by you, and the reviewer should add a signed approval to the comment at review time.

Some guidelines, such those about as comments and indentation, are based purely on convention, rather than on clear technical merit. However, conventions are important for readability, thus most conventions are MUSTs.

A lot of our code was written before these guidelines existed. You should fix violations as you encounter them in the course of your normal coding. You must not fix violations en masse without warning other developers and coordinating with them, so as not to make the merging of large branches more difficult than it already is.

There are many topics for additional standardization not covered by current version of this document, but deferred to future versions.
  • File and directory structure
  • Packages and modularity
  • Threads and locking
  • How to add configurable components
  • CLOS style: initforms, slot and accessor names, etc.
  • Recommendations on max number of slots per class.
  • More concrete examples of good code:
    • exceptions
    • transactions, with retry
    • XML
    • typing
    • encapsulation / abstraction
    • class and slot names
    • etc.
  • When (not) to use conditional compilation:
    • modifying the product
    • conditional debugging/console output/etc.
    • "temporarily" commenting-out blocks of code
    • etc.
  • Use of i< and friends (premature optimization, bug introduction)
There are some basic principles for team software development that every developer must keep in mind. Whenever the detailed guidelines are inadequate, confusing or contradictory, refer back to these principles for guidance:
  • Every developer's code must be easy for another developer to read, understand, and modify — even if the first developer isn't around to explain it. (This is the "hit by a truck" principle.)
  • Everybody's code should look the same. Ideally, there should be no way to look at lines of code and recognize it as "Fred's code" by its style.
  • Don't be "clever" — do the simplest thing that could possibly work properly.
  • Be precise.
  • Be concise.
  • KISS — Keep It Simple, Stupid.
  • Use the smallest hammer for the job.
  • Use common sense.
  • Keep related code together. Minimize the amount of jumping around someone has to do to understand an area of code.

When making decisions about how to write a given piece of code, aim for the following -ilities in this priority order:

  • Usability by the customer
  • Debuggability/Testability
  • Readability/Comprehensibility
  • Extensibility/Modifiability
  • Efficiency (of the Lisp code at runtime)

Most of these are obvious.

Usability by the customer means that the system has to do what the customer requires; it has to handle the customer's transaction volumes, uptime requirements; etc.

For the Lisp efficiency point, given two options of equivalent complexity, pick the one that performs better. (This is often the same as the one that conses less, i.e. allocates less storage from the heap.)

Given two options where one is more complex than the other, pick the simpler option and revisit the decision only if profiling shows it to be a performance bottleneck.

However, avoid premature optimization. Don't add complexity to speed up something that runs rarely, since in the long run, it matters less whether such code is fast.

To build code that is robust and maintainable, it matters a lot how the code is divided into components, how these components communicate, how changes propagate as they evolve, and more importantly how the programmers who develop these components communicate as these components evolve.

If your work affects other groups, might be reusable across groups, adds new components, has an impact on other groups (including QA or Ops), or otherwise isn't purely local, you must write it up using at least a couple of paragraphs, and get a design approval from the other parties involved before starting to write code — or be ready to scratch what you have when they object.

If you don't know or don't care about these issues, ask someone who does.

Often, the smallest hammer is to use an existing library. Or one that doesn't exist yet. In such cases, you are encouraged to use or develop such a library, but you must take appropriate precautions.
  • You MUST NOT start a new library unless you established that none is already available that can be fixed or completed into becoming what you need. That's a rule against the NIH syndrome ("Not Invented Here"), which is particularly strong amongst Lisp hackers.
  • Whichever library, old or new, you pick, you MUST get permission to incorporate third-party code into the code base. You must discuss the use of such library in the appropriate mailing-list, and have your code reviewed by people knowledgeable in the domain and/or the Lisp library ecosystem (if any). Please be ready to argue why this particular solution makes sense as compared to other available libraries.
  • Some libraries are distributed under licenses not compatible with the software you're writing, and must not be considered available for use. Be aware of these issues, or consult with people who are.

Note that if you have a "clever" implementation trick, and your trick really is clever, then you must definitely not include it in business specific code; but it may have its place in an open-source library used by the code. If your idea is not general purpose enough to have any users beyond your regular business users, then it is definitely either not clever enough or way too clever, and in either case does not belong in the code.

If you write a general-purpose library, or modify an existing open-source library, you are encouraged to publish the result separate from your main project and then have your project import it like any other open-source library.

Use your judgement to distinguish general-purpose versus business-specific code, and open-source the general-purpose parts, while keeping the business-specific parts a trade secret.

Open-Sourcing code has many advantages, including being able to leverage third parties for development, letting the development of features be user-directed, and keeping you honest with respect to code quality. Whatever code you write, you will have to maintain anyway, and make sure its quality is high enough to sustain use in production. There should therefore be no additional burden to Open-Sourcing, even of code that (at least initially) is not directly usable by third parties.

Development process is outside the scope of this document. However, developers should remember at least these bits: get reviewed, write tests, eliminate warnings, run tests, avoid mass-changes.

  • All code changes must be reviewed. You should expect that your code will be reviewed by other hackers, and that you will be assigned other hackers' code to review. Part of the review criteria will be that code obeys the coding standards in this document.
  • You must write and check-in tests for new code that you write and old bugs you fix. There must be a unit test for every API function, and any previously failing case. Your work is not truly done until this activity is complete. Estimating tasks must include the time it takes to produce such tests.
  • Your code must compile without any compilation error or warning messages whatsoever. If the compiler issues warnings that should be ignored, muffle those warnings using the xcvb-driver:with-controlled-compiler-conditions and xcvb-driver:*uninteresting-conditions* framework (also available as asdf-condition-control), either around the entire project, or around individual files (using asdf's :around-compile hooks).
  • You must run the "precheckin" tests successfully before committing code.
  • We should/will/must incorporate code coverage into our testing process. Tests are not sufficient if they do not cover all new and updated code; code that for whatever reason cannot be included in coverage results should be clearly marked as such including the reason.
  • Many people develop on branches. You must get permission to undertake mass-changes (e.g. mass reindentations) so that we can coordinate in advance, and give branch residents time to get back on the mainline

You must use correct spelling in your comments, and most importantly in your identifiers.

When several correct spellings exist (including American vs English), and there isn't a consensus amongst developers as which to use, you should choose the shorter spelling.

You must avoid using abbreviations for words, unless it's a word that is used very frequently, in which case you must use the same abbreviation consistently.

If you're not sure, consult a dictionary, Google for alternative spellings, or ask a local grammar nazi.

Here are examples of choosing the correct spelling:

  • Use "complimentary" in the sense of a meal or beverage that is not paid for by the recipient, not "complementary".
  • Use "existent" and "nonexistent", not "existant". Use "existence", not "existance".
  • Use "hierarchy" not "heirarchy".
  • Use "precede" not "preceed".
  • Use "weird", not "wierd".

Here are examples of choosing the shorter spelling:

  • Use "canceled", not "cancelled"
  • Use "queuing", not "queueing".
  • Use "signaled", not "signalled";
  • Use "traveled", not "travelled".
  • Use "aluminum", not "aluminium"
  • Use "oriented", not "orientated"
  • Use "color", not "colour"
  • Use "behavior", not "behaviour"

Make appropriate exceptions for industry standard nomenclature/jargon, including plain misspellings. For instance:

  • Use "referer", not "referrer", in the context of the HTTP protocol.
You should format source code so that no line is longer than 100 characters.

Some line length restriction is better than none at all. Google Java developers have adopted a 100-column limitation on source code lines and C++ developers limit themselves to 80 columns. Common Lispers at ITA have long adopted the 100-column limit. Allowing 100 columns seems better, since good style encourages the use of descriptive variables and function names.

Indent your code the way GNU Emacs does.

Indent carefully to make the code easier to understand.

By default, GNU Emacs does an excellent job indenting Common Lisp code. It can be taught how to indent new defining forms and special rules for domain specific languages. Each project may have some file to customize indentation; use it.

Use indentation to make complex function applications easier to read. When an application does not fit on one line or the function takes many arguments, consider inserting newlines between the arguments so that each one is on a separate line. However, do not insert newlines in a way that makes it hard to tell how many arguments the function takes or where an argument form starts and ends.

;; Bad (do-something first-argument second-argument (lambda (x) (frob x)) fourth-argument last-argument) ;; Better (do-something first-argument second-argument #'(lambda (x) (frob x)) fourth-argument last-argument)

You must include maintainership and other important information at the top of each source file.

You should not include a copyright statement in source files.

Every source file may begin with a one-line description of the file.

After that optional description, every source file may prominently include a statement about who originally wrote the code, made major changes, and/or is the current owner/maintainer. This makes it easier for hackers to locate whom to ask questions about the code, or to identify that no one is left to reply to such inquiries. However, consider that the information is not very helpful if it is not maintained; unless it brings information that cannot be easily extracted from source control, it is better skipped.

After that optional statement, every file should follow with a brief explanation of what the file contains.

After that explanation, every file should start the code itself with an (in-package :package-name) form.

After that in-package form, every file should follow with any file-specific (declaim (optimize ...)) declaration that is not covered by an asdf :around-compile hook.

;;;; Author: brown (Robert Brown) ;;;; Variable length encoding for integers and floating point numbers. (in-package #:varint) (declaim #.*optimize-default*)

You should not include copyright information in individual source code files. An exception is made for files meant to be disseminated as standalone.

Each project or library has a single file specifying its license. Absence of a LICENSE or COPYING file means the project is proprietary code.

Vertical white space: one blank line between top-level forms.

You should include one blank line between top-level forms, such as function definitions. Exceptionally, blank lines can be omitted between simple, closely related defining forms of the same kind, such as a group of related type declarations or constant definitions.

(defconstant +mix32+ #x12b9b0a1 "pi, an arbitrary number") (defconstant +mix64+ #x2b992ddfa23249d6 "more digits of pi") (defconstant +golden-ratio32+ #x9e3779b9 "the golden ratio") (defconstant +golden-ratio64+ #xe08c1d668b756f82 "more digits of the golden ratio") (defmacro incf32 (x y) "like incf, but for integers modulo 2**32" `(setf ,x (logand (+ ,x ,y) #xffffffff))) (defmacro incf64 (x y) "like incf, but for integers modulo 2**64" `(setf ,x (logand (+ ,x ,y) #xffffffffffffffff)))

Blank lines can be used to separate parts of a complicated function. Generally, however, you should break a large function into smaller ones instead of trying to make it more readable by adding vertical space. If you can't, you should document with a ;; comment what each of the separated parts of the function does.

Every top-level form should be fewer than 61 lines long, including comments but excluding the documentation string. This applies to each of the forms in an eval-when, rather than to the eval-when itself. Additionally, defpackage forms may be longer, since they may include long lists of symbols.

Horizontal white space: none around parentheses. No tabs.

You must not include extra horizontal whitespace before or after parentheses or around symbols.

You must not place right parentheses by themselves on a line. A set of consecutive trailing parentheses must appear on the same line.

;; Very Bad ( defun factorial ( limit ) ( let (( product 1 )) ( loop for i from 1 upto limit do (setf product ( * product i ) ) ) product ) ) ;; Better (defun factorial (limit) (let ((product 1)) (loop for i from 1 upto limit do (setf product (* product i))) product))

You should use only one space between forms.

You should not use spaces to vertically align forms in the middle of consecutive lines. An exception is made when the code possesses an important yet otherwise not visible symmetry that you want to emphasize.

;; Bad (let* ((low 1) (high 2) (sum (+ (* low low) (* high high)))) ...) ;; Better (let* ((low 1) (high 2) (sum (+ (* low low) (* high high)))) ...))

You must align nested forms if they occur across more than one line.

;; Bad (defun munge (a b c) (* (+ a b) c)) (defun munge (a b c) (* (+ a b) c))

The convention is that the body of a binding form is indented two spaces after the form. Any binding data before the body is usually indented four spaces. Arguments to a function call are aligned with the first argument; if the first argument is on its own line, it is aligned with the function name.

(multiple-value-bind (a b c d) (function-returning-four-values x y) (declare (ignore c)) (something-using a) (also-using b d))

An exception to the rule against lonely parentheses is made for an eval-when form around several definitions; in this case, include a comment ; eval-when after the closing parenthesis.

You must set your editor to avoid inserting tab characters in the files you edit. Tabs cause confusion when editors disagree on how many spaces they represent. In Emacs, do (setq-default indent-tabs-mode nil).

You should use document strings to explain how code works.

You must comment anything complicated so that the next developer can understand what's going on. (Again, the "hit by a truck" principle.)

Unless some bit of code is painfully self-explanatory, document it. Prefer documentation strings to comments because the former can be displayed by programming tools, such as IDEs, or by REPL queries such as (describe 'foo); they can also be extracted to create web-based documentation or other reference works.

Supply a documentation string (also known as docstring) when defining top-level functions, types, classes, and macros. Generally, add a documentation string wherever the language allows.

For functions, the docstring should describe the function's contract: what the function does, what the arguments mean, what values are returned, what conditions the function can signal. It should be expressed at the appropriate level of abstraction, explaining the intended meaning rather than, say, just the syntax. In documentation strings, capitalize the names of Lisp symbols, such as function arguments. For example, "The value of LENGTH should be an integer."

(defun primep (n) "Return T if N, an integer, is a prime number. Otherwise, return NIL." (cond ((or (< n 2)) nil) ((= n 2) t) ((divisorp 2 n) nil) (t (loop for i from 3 upto (sqrt n) by 2 do (when (divisorp i n) (return-from primep nil))) t))) (defgeneric table-clear (table) (:documentation "Like clrhash, empties the TABLE of all associations, and returns the table itself."))

A long docstring may usefully begin with a short, single-sentence summary, followed by the larger body of the docstring.

When the name of a type is used, the symbol may be quoted by surrounding it with a back quote at the beginning and a single quote at the end. Emacs will highlight the type, and the highlighting serves as a cue to the reader that M-. will lead to the symbol's definition.

(defun bag-tag-expected-itinerary (bag-tag) "Return a list of `legacy-pnr-pax-segment' objects representing the expected itinerary of the `bag-tag' object, BAG-TAG." ...)

Every method of a generic function should be independently documented when the specialization affects what the method does, beyond what is described in its generic function's docstring.

When you fix a bug, consider whether what the fixed code does is obviously correct or not; if not, you must add a comment explaining the reason for the code in terms of fixing the bug. Adding the bug number, if any, is also recommended.

You must use the appropriate number of semicolons to introduce comments.
  • File headers and important comments that apply to large sections of code in a source file should begin with four semicolons.
  • You should use three semicolons to begin comments that apply to just one top-level form or small group of top-level forms.
  • Inside a top-level form, you should use two semicolons to begin a comment if it appears between lines.
  • You should use one semicolon if it is a parenthetical remark and occurs at the end of a line. You should use spaces to separate the comment from the code it refers to so the comment stands out. You should try to vertically align consecutive related end-of-line comments.
;;;; project-euler.lisp ;;;; File-level comments or comments for large sections of code. ;;; Problems are described in more detail here: http://projecteuler.net/ ;;; Divisibility ;;; Comments that describe a group of definitions. (defun divisible-by (n d) (zerop (mod n d))) (defun proper-divisors (n) ...) (defun divisors (n) (cons n (proper-divisors n))) ;;; Prime numbers (defun prime-p (n) (cond ((or (< n 2)) nil) ((= n 2) t) ; parenthetical remark here ; continuation of the remark ((divisible-by n 2) nil) ; different remark ;; Comment that applies to a secion of code. (t (loop for i from 3 upto (sqrt n) by 2 do (when (divisible-by n i) (return-from prime-p nil))) t)))

You should include a space between the semicolon and the text of the comment.

You should punctuate documentation correctly.

When a comment is a full sentence, you should capitalize the initial letter of the first word and end the comment with a period. In general, you should use correct punctuation.

You must follow the ITA convention of using --- for comments requiring special attention, including unobvious tricks, TODO items, questions, breakage, danger.
  • ;--- prefixes a cautionary comment, e.g. explaining why the code in question is particularly tricky, delicate, or non-obvious.
  • ;---??? prefixes a serious question which needs to be resolved soon, by fixing either the code or its documentation.
  • ;---!!! identifies code which is broken, but which for some reason you cannot fix at this time. You should not use this often for new code.
  • ;---*** identifies active DANGER, for instance where important functionality is stubbed out, or a large design issue remains unresolved. Anything so marked must be fixed before code is rolled into production.

You must sign and date any of the above "requiring further attention" comments (but not mere cautionary explanations).

This strategy ensures that grepping for ;--- will always yield all the comments that require caution, as well as whom to talk to about each one.

Only use ;--- on the first line of such a comment. Other lines should use spaces to align vertically. This way, grepping will also yield a count of the number of issues.

You should insert a space after this comment prefix.

You may use these with multiple-semicolon comments as well.

Some people like to use words like FIXME or TODO. You may use these, but they must be preceded with ---.

Use TODO comments when the code is known to be incomplete and you want to indicate what work remains to be done.

The comments begin with TODO in all capital letters, followed by your email address or other identifier in parentheses, followed by a colon, a space, and an explanation of what additional work is desirable or required. The user name included in the comment is that of a person who understands the deficiency. A TODO comment is not a commitment to fix the problem.

When signing comments, you should use your username (for code within the company) or full email address (for code visible outside the company), not just initials.

;;--- TODO(george@gmail.com): Refactor to provide a better API.

Be specific when indicating times or software releases in a TODO comment:

;;--- TODO(brown): Remove this code after release 1.7 or before November, 2012.
You should document DSLs and any terse program in a DSL.

You should design your Domain Specific Language to be easy to read and understand by people familiar with the domain.

You must properly document all your Domain Specific Language.

Sometimes, your DSL is designed for terseness. In that case, it is important to document what each program does, if it's not painfully obvious from the context.

Notably, when you use regular expressions (e.g. with the CL-PPCRE package), you MUST ALWAYS put in a comment (usually a two-semicolon comment on the previous line) explaining, at least basically, what the regular expression does, or what the purpose of using it is. The comment need not spell out every bit of the syntax, but it should be possible for someone to follow the logic of the code without actually parsing the regular expression.

You should use lower case. You should not abbreviate. You should follow punctuation conventions.

Use lower case for all symbols. Consistently using lower case makes searching for symbol names easier and is more readable.

Note that Common Lisp is case-converting, and that the symbol-name of your symbols will be upper case. Because of this case-converting, attempts to distinguish symbols by case are defeated, and only result in confusion. While it is possible to escape characters in symbols to force lower case, you should not use this capability unless this is somehow necessary to interoperate with third-party software.

Place hyphens between all the words in a symbol. If you can't easily say an identifier out loud, it is probably badly named.

You must not use "/" or "." instead of "-" unless you have a well-documented overarching reason to, and permission from other hackers who review your proposal.

Generally, you should do not abbreviate words. You must avoid using abbreviations for words, unless it's a word that is used very frequently, in which case you must use the same abbreviation consistently. Abbreviations may also be used sparingly to avoid overly-long symbol names; it's easy to run into 100-column limit when there are very long names! You must especially avoid inconsistent abbreviations in exported names. For lexical variables of limited scope, abbreviations are fine.

;; Bad (defvar *default-username* "Ann") (defvar *max-widget-cnt* 200) ;; Better (defvar *default-user-name* "Ann") (defvar *maximum-widget-count* 200)

There are conventions in Common Lisp for the use of punctuation in symbols. You should not use punctuation in symbols outside these conventions.

Unless the scope of a variable is very small, do not use overly short names like i and zq.

Name your variables according to their intent, not their content.

You should name a variable according to the high-level concept that it represents, not according to the low-level implementation details of how the concept is represented.

Thus, you should avoid embedding data structure or aggregate type names, such as list, array, or hash-table inside variable names, unless you're writing a generic algorithm that applies to arbitrary lists, arrays, hash-tables, etc. In that case it's perfectly OK to name a variable list or array.

Indeed, you should be introducing new abstract data types with DEFCLASS or DEFTYPE, whenever a new kind of intent appears for objects in your protocols. Functions that manipulate such objects generically may then use variables the name of which reflect that abstract type.

For example, if a variable's value is always a row (or is either a row or NIL), it's good to call it row or first-row or something like that. It is alright is row has been DEFTYPE'd to STRING — precisely because you have abstracted the detail away, and the remaining salient point is that it is a row. You should not name the variable STRING in this context, except possibly in low-level functions that specifically manipulate the innards of rows to provide the suitable abstraction.

Be consistent. If a variable is named row in one function, and its value is being passed to a second function, then call it row rather than, say, value (this was a real case).

You should not include a library or package name as a symbol prefix when naming entities from the library or package.

When naming public symbols of a package, you should not include as a prefix the package name. Naming functions this way makes them awkward to use from client package with package-qualified symbols.

;; Bad (in-package #:varint) (defun varint-length64 () ... ) (in-package #:client-code) (defconst +padding+ (varint:varint-length64 +end-token+)) ;; Better (in-package #:varint) (defun length64 () ... ) (in-package #:client-code) (defconst +padding+ (varint:length64 +end-token+))

An exception to the above rule would be to include a prefix for the names of variables that would otherwise be expected to clash with variables in packages that use the current one. For instance, ASDF exports a variable *asdf-verbose* that controls the verbosity of asdf only and the entire Lisp programs.

Name globals according to convention.

The names of global constants should start and end with plus characters.

Global variable names should start and end with asterisks (also known in this context as earmuffs).

In some projects, parameters that are not meant to be usually modified or bound under normal circumstances (but may be during experimentation or exceptional situations) should start (but do not end) with a dollar sign. If such a convention exists within your project, you should follow it consistently. Otherwise, you should avoid naming variables like this.

Common Lisp does not have global lexical variables, so a naming convention is used to ensure that globals, which are dynamically bound, never have names that overlap with local variables. It is possible to fake global lexical variables with a differently named global variable and a DEFINE-SYMBOL-MACRO. You should not use this trick.

(defconstant +hash-results+ #xbd49d10d10cbee50) (defvar *maximum-search-depth* 100)
Names of predicate functions end with a "P".

Name boolean-valued functions with a trailing "P" or "-P", to indicate they are predicates. Generally, you should use "P" when the rest of the function name is one word and "-P" when it is more than one word.

For uniformity, you should follow the convention above, and not one of the alternatives below.

Alternative rules used in some existing packages is to always use "-P", or to always use "?". When you develop such a package, you must be consistent with the rest of the package. When you start a new package, you should not use such an alternative rule without a very good reason.

You should avoid side-effects when they are not necessary.

Lisp is best used as a "mostly functional" language.

Avoid modifying local variables, try rebinding instead.

Avoid creating objects and the SETFing their slots. It's better to set the slots during initialization.

Make classes as immutable as possible, that is, avoid giving slots setter functions if at all possible.

Using a mostly functional style makes it much easier to write concurrent code that is thread-safe. It also makes it easier to test the code.

Favor iteration over recursion.

Common Lisp systems are not required to implement constant space optimizations for recursive function calls from tail positions; however, most serious implementations (including SBCL and CCL) do implement proper tail calls. Still, even compilers that implement proper tail call do it only in restricted conditions:

  • The (DECLARE (OPTIMIZE ...)) settings must favor SPEED enough and not favor DEBUG too much, for some compiler-dependent meanings of "enough" and "too much". (For instance, in SBCL, you should avoid (SPEED 0) and (DEBUG 3) to achieve tail call elimination.)
  • There should not be dynamic bindings around the call (even though some Scheme compilers are able to properly treat such dynamic bindings, called parameters in Scheme parlance).

For compatibility with all compilers and to avoid stack overflow when debugging, you should use iteration or the built in mapping functions rather than relying on proper tail calls.

If you do rely on proper tail calls, you must prominently document the fact, and take appropriate measures to ensure an appropriate compiler is used with appropriate optimization settings. For fully portable code, you may have to use trampolines instead.

Use special variables sparingly.

Using Lisp "special" (dynamically bound) variables as implicit arguments to functions should be used sparingly, and only in cases where it won't surprise the person reading the code, and where it offers significant benefits.

Good candidates for such special variables are items for which "the current" can be naturally used as prefix, such as "the current database connection" or "the current business data source". They are singletons as far as the rest of the code is concerned, and often passing them as an explicit argument does not add anything to the readability or maintainability of the source code in question.

They can make it easier to write code that can be refactored. If you have a request processing chain, with a number of layers that all operate upon a "current" request, passing the request object explicitly to every function requires that every function in the chain have a request argument. Factoring out code into new functions often requires that these functions also have this argument, which clutters the code with boilerplate.

Note that a Lisp special variable is not a global variable in the sense of a global variable in, say, BASIC or C. As special variables can be dynamically bound, they are much more powerful than global value cells that can be changed from everywhere.

You should treat special variables as though they are per-thread variables. That is, leave the special variable with no top-level binding at all, and each thread of control that needs the variable should bind it explicitly. This will mean that any incorrect use of the variable will result in an "unbound variable" error, and each thread will see its own value for the variable.

Be consistent in assignment forms.

There are several styles for dealing with assignment and side-effects; whichever a given package is using, keep using the same consistently when hacking said package. Pick a style that makes sense when starting a new package.

Regarding multiple assignment in a same form, there are two schools: the first style groups as many assignments as possible into a single SETF or PSETF form thus minimizing the number of forms with side-effects; the second style splits assignments into as many individual SETF (or SETQ, see below) forms as possible, to maximize the chances of locating forms that modify a kind of place by grepping for (setf (foo .... A grep pattern must actually contain as many place-modifying forms as you may use in your programs, which may make this rationale either convincing or moot depending on the rest of the style of your code. You should follow the convention used in the package you are hacking. We recommend the first convention for new packages.

Regarding SETF and SETQ, there are two schools: this first regards SETQ as an archaic implementation detail, and avoids it entirely in favor of SETF; the second regards SETF as an additional layer of complexity, and avoids it in favor of SETQ whenever possible (i.e. whenever the assigned place is a variable or symbol-macro). You should follow the convention used in the package you are hacking. We recommend the first convention for new packages.

In the spirit of a mostly pure functional style, which makes testing and maintenance easier, we invite you to consider how to do things with the fewest assignments required.

Use packages appropriately.

Lisp packages are used to demarcate namespaces. Usually, each system has its own namespace. A package has a set of external symbols, which are intended to be used from outside the package, in order to allow other modules to use this module's facilities.

The internal symbols of a package should never be referred to from other packages. That is, you should never have to use the double-colon :: construct. (e.g. QUAKE::HIDDEN-FUNCTION). If you need to use double-colons to write real production code, something is wrong and needs to be fixed.

As an exception, unit tests may use the internals of the package being tested. So when you refactor, look at the package's unit tests.

The :: construct is also useful for very temporary hacks, and at the REPL. But if the symbol really is part of the externally-visible definition of the package, export it.

Each package is one of two types:

  • Intended to be included in the :use specification of other packages. If package A "uses" package B, then the external symbols of package B can be referenced from within package A without a package prefix. We mainly use this for low-level modules that provide widely-used facilities.
  • Not intended to be "used". To reference a facility provided by package B, code in package A must use an explicit package prefix, e.g. B:DO-THIS.

If you add a new package, it should always be of the second type, unless you have a special reason and get permission. Usually a package is designed to be one or the other, by virtue of the names of the functions. For example, if you have an abstraction called FIFO, and it were in a package of the first type you'd have functions named things like FIFO-ADD-TO and FIFO-CLEAR-ALL. If you used a package of the second type, you'd have names like ADD-TO and CLEAR-ALL, because the callers would be saying FIFO:ADD-TO and FIFO:CLEAR-ALL. (FIFO:FIFO-CLEAR-ALL is redundant and ugly.)

Another good thing about packages is that your symbol names won't "collide" with the names of other packages, except the ones your packages "uses". So you have to stay away from symbols that are part of the Lisp implementation (since you always "use" that) and that are part of any other packages you "use", but otherwise you are free to make up your own names, even short ones, and not worry about some else having used the same name. You're isolated from each other.

Your package must not shadow (and thus effectively redefine) symbols that are part of the Common Lisp language. There are certain exceptions, but they should be very well-justified and extremely rare:

  • If you are explicitly replacing a Common Lisp symbol by a safer or more featureful version.
  • If you are defining a package not meant to be "used", and have a good reason to export a symbol that clashes with Common Lisp, such as log:error and log:warn and so on.
You must make proper usage of assertions and conditions.
  • ASSERT should be used ONLY to detect internal bugs. Code should ASSERT invariants whose failure indicates that the software is itself broken. Incorrect input should be handled properly at runtime, and must not cause an assertion violation. The audience for an ASSERT failure is a developer. Do not use the data-form and argument-form in ASSERT to specify a condition to signal. It's fine to use them to print out a message for debugging purposes (and since it's only for debugging, there's no issue of internationalization).
  • CHECK-TYPE, ETYPECASE are also forms of assertion. When one of these fails, that's a detected bug. You should prefer to use CHECK-TYPE over (DECLARE (TYPE ...)) for the inputs of functions.
  • Your code should use assertions and type checks liberally. The sooner a bug is discovered, the better! Only code in the critical path for performance and internal helpers should eschew explicit assertions and type checks.
  • Invalid input, such as files that are read but do not conform to the expected format, should not be treated as assertion violations. Always check to make sure that input is valid, and take appropriate action if it is not, such as signalling a real error.
  • ERROR should be used to detect problems with user data, requests, permissions, etc., or to report "unusual outcomes" to the caller.
  • ERROR should always be called with an explicit condition type; it should never simply be called with a string. This enables internationalization.
  • Functions that report unusual outcomes by signaling a condition should say so explicitly in their contracts (their textual descriptions, in documentation and docstrings etc.). When a function signals a condition that is not specified by its contract, that's a bug. The contract should specify the condition class(es) clearly. The function may then signal any condition that is a type-of any of those conditions. That is, signaling instances of subclasses of the documented condition classes is fine.
  • Complex bug-checks may need to use ERROR instead of ASSERT.
  • When writing a server, you must not call WARN. Instead, you should use the appropriate logging framework.
  • Code must not call SIGNAL. Instead, use ERROR or ASSERT.
  • Code should not use THROW and CATCH; instead use the restart facility.
  • Code should not generically handle all conditions, e.g. type T, or use IGNORE-ERRORS. Instead, let unknown conditions propagate to the standard ultimate handler for processing.
  • There are a few places where handling all conditions is appropriate, but they are rare. The problem is that handling all conditions can mask program bugs. If you do need to handle "all conditions", you MUST handle only ERROR, not T and not SERIOUS-CONDITION. (This is notably because CCL's process shutdown depends on being able to signal process-reset and have it handled by CCL's handler, so we must not interpose our own handler.)
  • (error (make-condition 'foo-error ...)) is equivalent to (error 'foo-error ...) — code must use the shorter form.
  • Code should not signal conditions from inside the cleanup form of UNWIND-PROTECT (unless they are always handled inside the cleanup form), or otherwise do non-local exits from cleanup handers outside of the handler e.g. INVOKE-RESTART.
  • Do not clean up by resignaling. If you do that, and the condition is not handled, the stack trace will halt at the point of the resignal, hiding the rest. And the rest is the part we really care about! ;; WRONG WAY (handler-case (catch 'ticket-at (etd-process-blocks)) (error (c) (reset-parser-values) (error c))) ;; RIGHT WAY (unwind-protect (catch 'ticket-at (etd-process-blocks)) (reset-parser-values))
If you know the type of something, you should make it explicit in order to enable compile-time and run-time sanity-checking.

If your function is using a special variable as an implicit argument, it's good to put in a CHECK-TYPE for the special variable, for two reasons: to clue in the person reading the code that this variable is being used implicitly as an argument, and also to help detect bugs.

Using (declare (type ...)) is the least-desirable mechanism to use because, as Scott McKay puts it:

The fact is, (declare (type ...)) does different things depending on the compiler settings of speed, safety, etc. In some compilers, when speed is greater than safety, (declare (type ...)) will tell the compiler "please assume that these variables have these types" without generating any type-checks. That is, if some variable has the value 1432 in it, and you declare it to be of type string, the compiler might just go ahead and use it as though it's a string.

Moral: don't use (declare (type ...)) to declare the contract of any API functions, it's not the right thing. Sure, use it for "helper" functions, but not API functions.

Use macros when appropriate, which should be sparingly and carefully.

You must never use a macro where a function will do. That is, if the semantics of what you are writing conforms to the semantics of a function, then write it as a function rather than a macro.

You must not use a macro for performance reasons. If profiling shows that you have a performance problem with a specific function, document the need and profiling-results appropriately, and DECLAIM to that function INLINE.

You can also use "compiler-macros" as a way to speed up function execution by specifying a source-to-source transformation. Beware that it interferes with tracing the optimized functions.

When you write a macro-defining macro (a macro that generates macros), comment it particularly clearly, since these are hard for the uninitiated to understand.

Using Lisp macros properly requires taste. Avoid writing complicated macros unless the benefit clearly outweighs the cost. It takes more effort for your fellow developers to learn your macro, so you should only use a macro if the gain in expressiveness is big enough to justify that cost. As usual, feel free to consult your colleagues if you're not sure, since without a lot of Lisp experience, it can be hard to make this judgment.

You must not define new reader macros.

If your macro has a parameter that is a Lisp form that will be evaluated when the expanded code is run, you should name the parameter with the suffix -form. This convention helps make it clearer to the macro's user which parameters are Lisp forms to be evaluated, and which are not.

One way to write a macro is the so-called "call-with" style, explained at length in http://random-state.net/log/3390120648.html. The idea is to keep the macro very simple, generating a call to an auxiliary function, which often takes a functional argument consisting of code in the original macro call. Advantages: during development, you can modify the function instead of recompiling all macro call sites; during debugging, you can see the function in the stack trace; there is less generated code so smaller memory usage. You should use this style unless the macro body is simple, rarely subject to change, and the macro is used in tight loops where performance matters. Think about whether the extra stack frames are helpful or just clutter.

Any functions (closures) created by the macro should be named: either use FLET or NAMED-LAMBDA. Using FLET is also good because you can declare the function to be of dynamic extent (if it is — and usually it is).

If a macro call contains a form, and the macro expansion includes more than one copy of that form, the form can be evaluated more than once. If someone uses the macro and calls it with a form that has side effects or that takes a long time to compute, the behavior will be undesirable (unless you're intentionally writing a control structure such as a loop). A convenient way to avoid this problem is to evaluate the form only once, and bind a (generated) variable to the result. There is a very useful macro called ALEXANDRIA:ONCE-ONLY that generates code to do this. See also ALEXANDRIA:WITH-GENSYMS, to make some temporary variables in the generated code.

When you write a macro with a body, such as a WITH-xxx macro, even if there aren't any parameters, you should leave space for them anyway. For example, if you invent WITH-LIGHTS-ON, do not make the call to it look like (defmacro with-lights-on (&body b) ...). Instead, do (defmacro with-lights-on (() &body b) ...). That way, if parameters are needed in the future, you can add them without necessarily having to change all the uses of the macro.

You should use #. sparingly, and you must avoid read-time side-effects.

The #. standard read-macro will read one object, evaluate the object, and have the reader return the resulting value.

It is mainly used as a quick way to get something evaluated at compile time (actually "read time" but it amounts to the same thing). If you use this, the evaluation MUST NOT have any side effects and MUST NOT depend on any variable global state. The #. should be treated as a way to force "constant-folding" that a sufficiently-clever compiler could have figure out all by itself, when the compiler isn't sufficiently-clever and the difference matters. Consider using a DEFCONSTANT and its variants, which would give the value a name explaining what it means.

EVAL-WHEN is tricky. Be aware.

Lisp evaluation happens at several "times". Be aware of them when writing macros. EVAL-WHEN considered harmful to your mental health.

In summary of the article linked above, unless you're doing truly advanced macrology, the only valid combination in an EVAL-WHEN is to include all of (eval-when (:compile-toplevel :load-toplevel :execute) ...)

It is usually an error to omit the :execute, for it prevents LOADing the source rather than the fasl. It is usually an error to omit the :load-toplevel (except to modify e.g. readtables and compile-time settings), for it prevents LOADing future files or interactively compiling code that depend on the effects that happen at compile-time unless the current file was COMPILE-FILEd within the same Lisp session.

In some odd cases, you may want to evaluate things from within the expansion of a DEFTYPE or of a non-top-level DEFMACRO. In these cases, you should use ASDF-FINALIZERS and its ASDF-FINALIZERS:EVAL-AT-TOPLEVEL form.

Use CLOS appropriately.

When a generic function is intended to be called from other modules (other parts of the code), there should be an explicit DEFGENERIC form, with a :DOCUMENTATION string explaining the generic contract of the function (as opposed to its behavior for some specific class). It's generally good to do explicit DEFGENERIC forms, but for module entry points it is mandatory.

When the argument list of a generic function includes &KEY, the DEFGENERIC should always explicitly list all of the keyword arguments that are acceptable, and explain what they mean. (Common Lisp does not require this, but it is good form, and it may avoid spurious warnings on SBCL.)

You should avoid SLOT-VALUE and WITH-SLOTS, unless you absolutely intend to circumvent any sort of method combination that might be in effect for the slot. Rare exceptions include INITIALIZE-INSTANCE and PRINT-OBJECT methods and the initialization of Quake volatile slots in INITIALIZE-RECORD methods. Otherwise, you should use accessors, WITH-ACCESSORS

Accessor names generally follow a convention of <protocol-name>-<slot-name>, where an "protocol" in this case loosely indicates a set of functions with well-defined behavior; a class can implement all or part of an interface by defining some methods for (generic) functions in the protocol, including readers and writers. No implication of a formal "protocol" concept is intended.

For example, if there were a "notional" protocol called is pnr with accessors pnr-segments and pnr-passengers, then the classes air-pnr, hotel-pnr and car-pnr could each reasonably implement methods for pnr-segments and pnr-passengers as accessors.

By default, an abstract base class name is used as the notional protocol name, so accessor names default to <class-name>-<slot-name>; while such names are thus quite prevalent, this form is neither required nor even preferred. In general, it contributes to "symbol bloat", and in many cases has led to a proliferation of "trampoline" methods.

Accessors named <slot-name>-of should not be used.

Explicit DEFGENERIC forms should be used when there are (or it is anticipated that there will be) more than one DEFMETHOD for that generic function. The reason is that the documentation for the generic function explains the abstract contract for the function, as opposed to explaining what an individual method does for some specific class(es).

You must not use generic functions where there is no "notional" protocol. To put it more concretely, if you have more than one generic function that specializes its Nth argument, the specializing classes should all be descendants of a single class. Generic functions must not be used for "overloading", i.e. simply to use the same name for two entirely unrelated types.

More precisely, it's not really whether they descend from a common superclass, but whether they obey the same "protocol". That is, the two classes should handle the same set of generic functions, as if there were an explicit DEFGENERIC for each method.

Here's another way to put it. Suppose you have two classes, A and B, and a generic function F. There are two methods for F, which dispatch on an argument being of types A and B. Is it plausible that there might be a function call somewhere in the program that calls F, in which the argument might sometimes, at runtime, be of class A and other times be of class B? If not, you probably are overloading and should not be using a single generic function.

We allow one exception to this rule: it's OK to do overloading if the corresponding argument "means" the same thing. Typically one overloading allows an X object, and the other allows the name of an X object, which might be a symbol or something.

You must not use MOP "intercessory" operations.

If a class definition creates a method as a :READER, :WRITER, or :ACCESSOR, do not redefine that method. It's OK to add :BEFORE, :AFTER, and :AROUND methods, but don't override the primary method.

In methods with keyword arguments, you must always use &KEY, even if the method does not care about the values of any keys, and you should never use &ALLOW-OTHER-KEYS. As long as a keyword is accepted by any method of a generic function, it's OK to use it in the generic function, even if the other methods of the same generic function don't mention it explicitly. This is particularly important for INITIALIZE-INSTANCE methods, since if you did use &ALLOW-OTHER-KEYS, it would disable error checking for misspelled or wrong keywords in MAKE-INSTANCE calls!

A typical PRINT-OBJECT method might look like this:

(defmethod print-object ((p person) stream) (print-unprintable-object (p stream :type t :identity t) (with-slots (first-name last-name) p (safe-format stream "~a ~a" first-name last-name))))
Appropriately use or avoid to use NIL.

NIL can have several different interpretations:

  • "False." In this case, use NIL. You should test for false NIL using the operator NOT or using the predicate function NULL.
  • "Empty-list." In this case, use '(). (Be careful about quoting the empty-list when calling macros.) You should use ENDP to test for the empty list when the argument is known to be a proper list, or with NULL otherwise.
  • A statement about some value being unspecified. In this case, you may use NIL if there is no risk of ambiguity anywhere in your code; otherwise you should use an explicit, descriptive symbol.
  • A statement about some value being known not to exist. In this case, you should use an explicit, descriptive symbol instead of NIL.

You must not introduce ambiguity in your data representations that will cause headaches for whoever has to debug code. If there is any risk of ambiguity, you should use an explicit, descriptive symbol or keyword for each case, instead of using NIL for either. If you do use NIL, you must make sure that the distinction is well documented.

When working with database classes, keep in mind that NIL need not always map to 'NULL' (and vice-versa)! The needs of the database may differ from the needs of the Lisp.

You should select proper data representation. You should not abuse the LIST data structure.

Common Lisp makes it especially easy to use its builtin (single-linked) LIST data structure. However, you should only use this data structure where it is appropriate.

You must not use lists when they are an inappropriate abstraction for the data being manipulated.

You must only use lists when their performance characteristics is appropriate for the algorithm at hand (i.e. sequential iteration over the entire contents).

An exception is when it is known in advance that the size of the list will remain very short (say, less than 16 elements), especially so when manipulating source code at compile-time.

Another exception is for introducing literal constants that will be transformed into more appropriate data structures at compile-time or load-time.

You should use the appropriate representation for product types.

You should avoid using a list as anything besides a container of elements of like type. You must not use a list as method of passing multiple separate values of different types in and out of function calls. Sometimes it is convenient to use a list as a little ad hoc structure, i.e. "the first element of the list is a FOO, and the second is a BAR", but this should be used minimally since it gets harder to remember the little convention. You must only use a list that way when destructuring the list of arguments from a function, or creating a list of arguments to which to APPLY a function.

The proper way to pass around an object comprising several values of heterogeneous types is to use a structure as defined by DEFSTRUCT or DEFCLASS.

You should use multiple values only when function returns a small number of values that are meant to be destructured immediately by the caller, rather than passed together as arguments to further functions.

You should not return a condition object as one of a set of multiple values. Instead, you should signal the condition to denote an unusual outcome.

You should signal a condition to denote an unusual outcome, rather than relying on a special return type.

Use the appropriate functions when manipulating lists.

Use FIRST to access the first element of a list, SECOND to access the second element, etc. Use REST to access the tail of a list. Use ENDP to test for the end of the list.

Use CAR and CDR when the cons cell is not being used to implement a proper list and is instead being treated as a pair of more general objects. Use NULL to test for NIL in this context.

The latter case should be rare outside of alists, since you should be using structures and classes where they apply, and data structure libraries when you want trees.

Exceptionally, you may use CDADR and other variants on lists when manually destructuring them, instead of using a combination of several list accessor functions. In this context, using CAR and CDR instead of FIRST and REST also makes sense. However, mind in such cases that it might be more appropriate to use higher-level constructs such as DESTRUCTURING-BIND or FARE-MATCHER:MATCH.

You should use arrays rather than lists where random access matters.

ELT has O(n) behavior when used on lists. If you are to use random element access on an object, use arrays and AREF instead.

The exception is for code outside the critical path where the list is known to be small anyway.

You should only use lists as sets for very small lists.

Using lists as representations of sets is a bad idea unless you know the lists will be small, for accessors are O(n) instead of O(log n). For arbitrary big sets, use balanced binary trees, for instance using lisp-interface-library.

If you still use lists as sets, you should not UNION lists just to search them.

(member foo (union list-1 list-2)) ; Bad (or (member foo list-1) (member foo list-2)) ; Better

Indeed, UNION not only conses unnecessarily, but it can be O(n^2) on some implementations, and is rather slow even when it's O(n).

You should usually refer to a function as #'FUN rather than 'FUN.

The former refers to the function object, as is properly scoped. The latter refers to the symbol, which when called uses the global FDEFINITION of the symbol.

When using functions that take a functional argument (e.g., MAPCAR, APPLY, :TEST and :KEY arguments), you should use the #' to quote the function, not just single quote.

An exception is when you explicitly want dynamic linking, because you anticipate that the global function binding will be updated.

Another exception is when you explicitly want to access a global function binding, and avoid a possible shadowing lexical binding. This shouldn't happen often, as it is usually a bad idea to shadow a function when you will want to use the shadowed function; just use a different name for the lexical function.

You must consistently use either #'(lambda ...) or (lambda ...) without #' everywhere. You should only use the former style if your code is intended as a library with maximal compatibility to all Common Lisp implementations. Unlike the case of #'symbol vs 'symbol, it is only a syntactic difference with no semantic impact, except that the former works on Genera and the latter doesn't.

Note that if you start writing a new system in a heavily functional style, you may consider using LAMBDA-READER, a system that lets you use the unicode character λ instead of LAMBDA. But you must not start using such a syntactic extension in an existing system without getting permission from other developers.

Common Lisp pathnames are tricky. Be aware of pitfalls.

It is surprisingly hard to properly deal with pathnames in Common Lisp.

First, be aware of the discrepancies between the syntax of Common Lisp pathnames, which depends on which implementation and operating system you are using, and the native syntax of pathnames on your operating system. The Lisp syntax may involves quoting of special characters such as #\. and #\*, etc., in addition to the quoting of #\\ and #\" within strings. By contrast, your operating system's other system programming languages (shell, C, scripting languages) may only have one layer of quoting, into strings.

Second, when using MERGE-PATHNAMES, be wary of the treatment of the HOST component, which matters a lot on non-Unix platforms. You probably should instead be using ASDF-UTILS:MERGE-PATHNAMES*.

Third, be aware that DIRECTORY is not portable in how it handles wildcards, sub-directories, symlinks, etc. There again, ASDF-UTILS provides several common abstractions to deal with pathnames.

Finally, be aware that paths may change between the time you build the Lisp image for your application, and the time you run the application from its image. You should be careful to reset your image to forget irrelevant build-time paths and reinitialize any search path from current environment variables. ASDF for instance requires you to reset its paths with ASDF:CLEAR-CONFIGURATION.

You must follow the proper usage regarding well-known functions, macros and special forms.

You must use proper defining forms for constant values.

The Lisp system we primarily use, SBCL, is very picky and signals a condition whenever a constant is redefined to a value not EQL to its previous setting. You must not use DEFCONSTANT when defining variables that are not numbers, characters, or symbols (including booleans and keywords). Instead, consistently use whichever alternative is recommended for your project.

;; Bad (defconstant +google-url+ "http://www.google.com/") (defconstant +valid-colors+ '(red green blue))

Open-Source libraries may use ALEXANDRIA:DEFINE-CONSTANT for constants other than numbers, characters and symbols (including booleans and keywords). You may use the :TEST keyword argument to specify an equality predicate.

;; Better, for Open-Source code: (define-constant +google-url+ "http://www.google.com/" :test #'string=) (define-constant +valid-colors+ '(red green blue))
You should make proper use of &OPTIONAL, &KEY, and &AUX arguments.

You should avoid using &ALLOW-OTHER-KEYS, since it blurs the contract of a function. Almost any real function (generic or not) allows a certain fixed set of keywords, as far as its caller is concerned, and those are part of its contract. If you are implementing a method of a generic function, and it does not need to know the values of some of the keyword arguments, it is acceptable to use &ALLOW-OTHER-KEYS rather than list all the keyword arguments explicitly and use (declare (ignore ...)) on them. (Of course in such a case there should not be a &REST.) Note that the contract of a generic function belongs in the DEFGENERIC, not in the DEFMETHOD which is basically an "implementation detail" of the generic function as far as the caller of the generic is concerned.

You should avoid using &AUX arguments, except in very short helper functions where they allow you to eschew a LET.

You should avoid having both &OPTIONAL and &KEY arguments, unless it never makes sense to specify keyword arguments when the optional arguments are not all specified. You must not have non-NIL defaults to your &OPTIONAL arguments when your function has both &OPTIONAL and &KEY arguments.

You should avoid excessive nesting of binding forms inside a function. If your function ends up with massive nesting, you should probably break it up into several functions or macros. If it is really a single conceptual unit, consider using a macro such as FARE-UTILS:NEST to at least reduce the amount of indentation required. It is bad form to use NEST in typical short functions with 4 or fewer levels of nesting, but also bad form not to use it in the exceptional long functions with 10 or more levels of nesting. Use your judgment and consult your reviewers.

Use the appropriate conditional form.

Use WHEN and UNLESS when there is only one alternative. Use IF when there are two alternatives and COND when there are several.

However, don't use PROGN for an IF clause — use COND, WHEN, or UNLESS.

Note that in Common Lisp, WHEN and UNLESS return NIL when the condition evaluates to NIL. Nevertheless, you may use an IF to explicitly return NIL if you have a specific reason to insist on the return value.

You should only use CASE and ECASE to compare integers, characters or symbols (including booleans and keywords). Indeed, CASE uses EQL for comparisons, so strings and other numbers may not compare the way you expect.

You must not use gratuitous single quotes in CASE forms. This is a common error:

(case x ('bar :bar) ('quux :quux))

'BAR there is (QUOTE BAR), meaning this leg of the case will be executed if X is QUOTE... and ditto for the second leg (though QUOTE will be caught by the first clause). This is unlikely to be what you really want.

In CASE forms, you must use otherwise instead of t when you mean "execute this clause if the others fail". And you must use ((t) ...) when you mean "match the symbol T".

You should use ECASE and ETYPECASE in preference to CASE and TYPECASE. You should not use CCASE or CTYPECASE at all.

You should the appropriate predicates when comparing objects.

Lisp provides four general equality predicates: EQ, EQL, EQUAL, and EQUALP, which subtly vary in semantics. Additionally, Lisp provides the type-specific predicates =, CHAR=, CHAR-EQUAL, STRING=, and STRING-EQUAL. Know the distinction!

You should use EQL to compare objects and symbols for identity.

You must not use EQ to compare numbers or characters. Two numbers or characters that are EQL are not required by Common Lisp to be EQ.

When choosing between EQ and EQL, you should use EQL unless you are writing performance-critical low-level code. EQL reduces the opportunity for a class of embarrassing errors (i.e. if characters are ever compared). There may a tiny performance cost relative to EQ, although under SBCL, it often compiles away entirely.

You should use CHAR= for case-dependent character comparisons, and CHAR-EQUAL for case-ignoring character comparisons.

You should use STRING= for case-dependent string comparisons, and STRING-EQUAL for case-ignoring string comparisons.

A common mistake when using SEARCH on strings is to provide STRING= or STRING-EQUAL as the :TEST function. The :TEST function is given two sequence elements to compare. If the sequences are strings, the :TEST function is called on two characters, so the correct tests are CHAR= or CHAR-EQUAL. If you use STRING= or STRING-EQUAL, the result is what you expect, but in some Lisp implementations it's much slower. CCL (at least as of 8/2008) creates a one-character string upon each comparison, for example, which is very expensive.

Also, you should use :START and :END arguments to STRING= or STRING-EQUAL instead of using SUBSEQ; e.g. (string-equal (subseq s1 2 6) s2) should instead be (string-equal s1 s2 :start1 2 :end1 6) This is preferable because it does not cons.

You should use ZEROP, PLUSP, or MINUSP, instead of comparing a value to 0 or 0.0.

You must not use exact comparison on floating point numbers, since the vague nature of floating point arithmetic can produce little "errors" in numeric value. You should compare absolute values to a threshhold.

You must use = to compare numbers, unless it's really okay for 0, 0.0 and -0.0 to compare unequal! But then again, you must not usually use exact comparison on floating point numbers.

Monetary amounts should be using decimal (rational) numbers to avoid the complexities and rounding errors of floating-point arithmetic.

Use the appropriate form for iteration.

You should simpler forms such as DOLIST or DOTIMES instead of LOOP in simple cases when you're not going to use any of the LOOP facilities such as bindings, collection or block return.

Use the WITH clause of LOOP when it will avoid a level of nesting with LET. You may use LET if it makes it clearer to return one of bound variables after the LOOP, rather than use a clumsy FINALLY (RETURN ...) form.

In the body of a DOTIMES, do not set the iteration variable. (CCL will issue a compiler warning if you do.)

Most systems use unadorned symbols in the current package as LOOP keywords. Other systems use actual :keywords from the KEYWORD package as LOOP keywords. You must be consistent with the convention used in your system.

Use the appropriate I/O functions.

When writing a server, code must not send output to the standard streams such as *STANDARD-OUTPUT* or *ERROR-OUTPUT*. Instead, code must use the proper logging framework to output messages for debugging. We are running as a server, so there is no console!

Code must not use PRINT-OBJECT to communicate with a user — PRINT-OBJECT is for debugging purposes only. Modifying any PRINT-OBJECT method must not break any public interfaces.

You should not use a sequence of WRITE-XXX where a single FORMAT string could be used. Using format allows you to parameterize the format control string in the future if the need arises.

You should use WRITE-CHAR to emit a character rather than WRITE-STRING to emit a single-character string.

You should not use (format nil "~A" value); you should use PRINC-TO-STRING instead.

You should use ~<Newline> or ~@<Newline> in format strings to keep them from wrapping in 100-column editor windows, or to indent sections or clauses to make them more readable.

You should not use STRING-UPCASE or STRING-DOWNCASE on format control parameters; instead, it should use "~:@(~A~)" or "~(~A~)".

Be careful when using the FORMAT conditional directive. The parameters are easy to forget.

No parameters, e.g. "~[Siamese~;Manx~;Persian~] Cat"
Take one format argument, which should be an integer. Use it to choose a clause. Clause numbers are zero-based. If the number is out of range, just print nothing. You can provide a default value by putting a ":" in front of the last ";". E.g. in "~[Siamese~;Manx~;Persian~:;Alley~] Cat", an out-of-range arg prints "Alley".
: parameter, e.g. "~:[Siamese~;Manx~]"
Take one format argument. If it's NIL, use the first clause, otherwise use the second clause.
@ parameter, e.g. "~@[Siamese ~a~]"
If the next format argument is true, use the choice, but do NOT take the argument. If it's false, take one format argument and print nothing. (Normally the clause uses the format argument.)
# parameter, e.g. "~#[ none~; ~s~; ~s and ~s~]"
Use the number of arguments to format as the number to choose a clause. The same as no parameters in all other ways. Here's the full hairy example: "Items:~#[ none~; ~S~; ~S and ~S~:;~@{~#[~; and~] ~S~^ ,~}~]."
You must not use INTERN or UNINTERN at runtime.

You must not use INTERN it at runtime. Not only does it cons, it either creates a permanent symbol that won't be collected or gives access to internal symbols. This creates opportunities for memory leaks, denial of service attacks, unauthorized access to internals, clashes with other symbols.

You must not INTERN a string just to compare it to a keyword; use STRING= or STRING-EQUAL.

(member (intern str :keyword) $keys) ; BAD (member str $keys :test #'string-equal) ; GOOD

You must not use UNINTERN at runtime. It can break code that relies on dynamic binding. It makes things harder to debug. You must not dynamically intern any new symbol, and therefore you need not dynamically unintern anything.

You may of course use INTERN at compile-time, in the implementation of some macros. Even so, it is usually more appropriate to use abstractions on top of it, such as ALEXANDRIA:SYMBOLICATE or ALEXANDRIA:FORMAT-SYMBOL to create the symbols you need.

You should not use EVAL.

Places where it is actually appropriate to use EVAL are so few and far between that you must get permission; it's easily misused.

If your code manipulates symbols at runtime and needs to get the value of a symbol, use SYMBOL-VALUE, not EVAL.

Often, what you really need is to write a macro, not to use EVAL.

Places where it is OK to use EVAL are: testing frameworks and code that is ONLY used for testing; the build infrastructure; and inside macros when there isn't any reasonable way to avoid using EVAL (there almost always is). Other uses need to be checked. We do have a few special cases where EVAL is allowed.

You should avoid unnecessary allocation of memory.

In a language with automatic storage management (such as Lisp or Java), the colloquial phrase "memory leak" refers to situation where storage that is not actually needed nevertheless does not get deallocated, because it is still reachable.

You should be careful that when you create objects, you don't leave them reachable after they are no longer needed!

Here's a particular trap-for-the-unwary in Common Lisp. If you make an array with a fill pointer, and put objects in it, and then set the fill pointer back to zero, those objects are still reachable as far as Lisp goes (the Common Lisp spec says that it's still OK to refer to the array entries past the end of the fill pointer).

Don't cons (i.e., allocate) unnecessarily. Garbage collection is not magic. Excessive allocation is usually a performance problem.

You should only use DYNAMIC-EXTENT where it matters for performance, and you can document why it is correct.

The purpose of the DYNAMIC-EXTENT declaration is to improve performance by reducing garbage collection in cases where it appears to be obvious that an object's lifetime is within the "dynamic extent" of a function. That means the object is created at some point after the function is called, and the object is always inaccessible after the function exits by any means.

By declaring a variable or a local function DYNAMIC-EXTENT, the programmer asserts to Lisp that any object that is ever a value of that variable or the closure that is the definition of the function has a lifetime within the dynamic extent of the (innermost) function that declares the variable.

The Lisp implementation is then free to use that information to make the program faster. Typically, Lisp implementations can take advantage of this knowledge to stack-allocate:

  • The lists created to store &REST parameters.
  • Lists and vector allocated within a function.
  • Closures.

If the assertion is wrong, i.e. if the programmer's claim is not true, the results can be catastrophic: Lisp can terminate any time after the function returns, or it hang forever, or — worst of all — produce incorrect results without any runtime error!

Even if the assertion is correct, future changes to the function might introduce a violation of the assertion. This increases the danger.

In most cases, such objects are ephemeral. Modern Lisp implementations use generational garbage collectors, which are quite efficient under these circumstances.

Therefore, DYNAMIC-EXTENT declarations should be used sparingly. You must only use them if:

  1. There is some good reason to think that the overall effect on performance is noticeable, and
  2. It is absolutely clear that the assertion is true.
  3. It is quite unlikely that the code will be changed in ways that cause the declaration not to be true anymore.

Point (1) is a special case of the principle of avoiding premature optimization. An optimization like this only matters if such objects are allocated at a very high rate, e.g. "inside an inner loop".

It's sometimes hard to know what the rate will be. When writing a function or macro that's part of a library of reusable code, there's no a priori way to know how often the code will run. Ideally, tools would be available to discover the availability and suitability of using such an optimization based on running simulations and test cases, but in practice this isn't as easy as it ought to be. It's a tradeoff. If you're very, very sure that the assertion is true (that the object is only used within the dynamic scope), and it's not obvious how much time will be saved and it's not easy to measure, then it may be better to put in the declaration than to leave it out. (Ideally it would be easier to make such measurements than it actually is.)

You must only use faster unsafe operations when there is a clear performance need and you can document why it's correct.

Some systems define unsafe numerical comparators, that are designed to be used with fixnums only, and are faster in that case, but incorrect in case of overflow, and have undefined behavior when called with anything but a fixnum. You must not use these functions without both profiling results indicating the need for this optimization, and careful documentation explaining why it is safe to use them.

You should use REDUCE instead of APPLY where appropriate.

You should use REDUCE instead of APPLY and a consed-up list, where the semantics of the first operator argument otherwise guarantees the same semantics. Of course, you must use APPLY if it does what you want and REDUCE doesn't.

For instance, (apply #'+ (mapcar #'acc frobs) should instead be (reduce #'+ frobs :key #'acc)

This is preferable because it does not do extra consing, and does not risk going beyond CALL-ARGUMENTS-LIMIT on implementations where that limit is small, which could blow away the stack on long lists (we want our code to not be gratuitously unportable).

However, you must be careful not to use REDUCE in ways that needlessly increase the complexity class of the computation. For instance, (REDUCE 'STRCAT ...) is O(n^2) when an appropriate implementation is only O(n). Moreover, (REDUCE 'APPEND ...) is also O(n^2) unless you specify :FROM-END T. In such cases, you must use proper abstractions that cover those cases instead of calling REDUCE, first defining them in a suitable library if needs be.

You should not use NCONC; you should use APPEND instead, or better data structures.

You should almost never use NCONC. You should use APPEND when you don't depend on any side-effect. You should use ALEXANDRIA:APPENDF when you need to update a variable. You should probably not depend on games being played with the CDR of the current cons cell; and if you do, you must include a prominent comment explaining the use of NCONC; and you should probably reconsider your data representation.

By extension, you should avoid MAPCAN or the NCONC feature of LOOP. You should instead respectively use ALEXANDRIA:MAPPEND and the APPEND feature of LOOP respectively.

NCONC is very seldom a good idea, since its time complexity class is no better than APPEND, its space complexity class also is no better than APPEND in the common case where no one else is sharing the side-effected list, and its bug complexity class is way higher than APPEND.

If the small performance hit due to APPEND vs. NCONC is a limiting factor in your program, you have a big problem and are probably using the wrong data structure: you should be using sequences with constant-time append (see Okasaki's book, and add them to lisp-interface-library), or more simply you should be accumulating data in a tree that will get flattened once in linear time after the accumulation phase is complete (see how ASDF does it).

You may only use NCONC, MAPCAN or the NCONC feature of LOOP in low-level functions where performance matters, where the use of lists as a data structure has been vetted because these lists are known to be short, and when the function or expression the result of which are accumulated explicitly promises in its contract that it only returns fresh lists. Even then, the use of such primitives must be rare, and accompanied by justifying documentation.


Credits: Adam Worrall, Dan Pierson, Matt Marjanovic, Matt Reklaitis, Paul Weiss, Scott McKay, Sundar Narasimhan, and several other people contributed. Special thanks to Steve Hain, and to the previous editors, in reverse chronological order Dan Weinreb and Jeremy Brown.

Revision 1.8

Robert Brown
François-René Rideau