diff --git a/lispguide.xml b/lispguide.xml new file mode 100644 index 0000000..7dc8e41 --- /dev/null +++ b/lispguide.xml @@ -0,0 +1,3215 @@ + + + +

+ +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. +
  3. + It is absolutely clear that the assertion is true. +
  4. +
  5. + It is quite unlikely that the code will be changed + in ways that cause the declaration not to be true anymore. +
  6. +
+

+ 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 +
+ + + +