LispStyle

This is my own personal and opinionated style guide for CommonLisp. Of course, since Lisp is inherently resistant to top-down control you can write it however you want, but these are my own tastes that've developed after a few years.

Table of Contents

Indentation

I've found through experience that 96 columns of text is the ideal indentation width for Lisp code, and the vast majority of large expressions will just fit under it. 80 columns, which is the standard for many other languages comes much too short for most programs to be pretty, and 100 columns which is also a common width for Lisp code, I find tends to be too long to easily scan with my eyes.

S-expressions should usually be indented according to a properly set up GNU Emacs with SLIME. There may be cases where you wish to manually indent expressions instead, for aesthetic purposes. I prefer to use a combination of tabs and spaces for indenting, and recall that despite what some editors may trick you into believing, tabs are eight spaces wide, always.

Variables

Defining Variables

You should export special variables that are settable user options, that a user program may want to set or dynamically bind. These variables should be marked with star earmuffs *like-this*. Special variables that are internal to the package don't always have to be marked this way, and often shouldn't, when it looks cleaner. It all really comes down to intention: for variables that are not meant to be treated specially in any case, the earmuffs should come off.

In the normal case, use DEFVAR. Some Lisp systems support DEFGLOBAL, which defines a lexical (i.e. non-special) global variable. This, if available should be used for simple variables that track some internal state like a meter, or would likely break some internal system by being modified. DEFPARAMETER should be used for variables that evaluate some load-time form, and/or benefit from being reset to their default values upon (re)loading.

Constant variables defined with DEFCONSTANT seem to be commonly marked with plus sign earmuffs +like-this+. I don't like using this convention for my own code, but I don't mind it if I need to use variables from other packages. Why? Where this convention originated is uncertain to me, but it isn't standard or apparent in a lot of codebases, especially older ones. All of the constants given in the CL standard, for instance don't follow this convention. I also just find it ugly.

A variable that starts with a percent sign "%" is most often some internal constant used in a lower-level operation, such as an offset into some number of bits.

Setting variables

Variables on their own should be set with SETQ. It's permissible to include them in larger SETF blocks.

Symbols that are known to be symbol macros rather than plain variables should always be set with SETF instead of SETQ; a common example of this is binding the slots of a class with the macro WITH-SLOTS. You should also prefer (SETF (VALUES ..) ..) over MULTIPLE-VALUE-SETQ in these cases. I believe it to be a misfeature that SETQ was made to accomodate symbol macros by acting like SETF. Why? Recall that (SETQ X Y) is meant to be equivalent to (SET 'X Y), however SET only does one job: it sets variables. I think it's ridiculous that one could expect anything to result from a SETQ other than the setting of a variable--at least when you use SETF you accept the consequences of macro magic.

Functions

Defining Functions

Functions that start with a percent sign "%" should denote "sub-primitive" functions that perform low-level operations which may easily violate storage conditions, safety, not type-check their arguments, or otherwise do things in a non-lispy way. It should in a sense be a warning to the programmer that this function is only intended for internal use, and care must be taken in order to use it properly. If a sub-primitive by itself performs some desirable action, it's useful to write a wrapper that can call it safely.

Lambda lists

Don't overuse keyword arguments, small and simple functions should take optional parameters instead. A function that can take a predicate is better off as an optional parameter instead of (f foo bar :test 'equalp) like most standard CL functions such as MEMBER. Large, complicated functions with a lot of defaultable parameters are a good use of keyword arguments.

Quoting

When you pass a function as a parameter, you can very often pass the symbol that the function is bound to. I tend to do this when passing a test to a routine such as MEMBER, though this is entirely a personal preference. There are other reasons you might want to call a symbol as a function. When directly applying a named function, or in any case where an explicit function is needed, you should sharp-quote.

Macros

SETF

I've seen a few people on various fora saying you should avoid SETF at all costs, because it's an unknowable plague of macro abstraction that will consume all of your living code. These people are morons.

Reader macros

TODO

CLOS

Class slots

Some people name class slots by beginning them with a percent sign, "%" and immediately define accessors, with a similar intention as naming functions this way: accessing the slot by itself (e.g. with SLOT-VALUE) is not the way in which the class is meant to be used, and might break something. However, I strongly reccomend against this for a simple reason: Accessing class slots in this way is not wrong.

When defining methods for a class, it is often desirable to wrap a WITH-SLOTS around the body of the method with the slots you intend to use, rather than using named accessor methods. Why? Simply, accessor methods /are/ methods, and it's not always knowable if you or someone else may override or add extra functionality to it, and this can have painful effects. It is also a matter of style, given that it is often much cleaner to access slots this way, especially when there are many of them. And, if you want to use the accessor methods the same way, you can use WITH-ACCESSORS.

Methods

Q: Should you always define functionality for a class in terms of a method? A: Not always. When you have a method that's internal and never defined for more than one class, you should make it a normal function instead.

Types

TODO


home // reverse // older // edit // modified: 2025.06.20 [Fri] 06:29