Slots and slot accessors

Slots are implementation details. As is known from software engineering, a module consists of an interface part and an implementation part. In Common Lisp, it is the same, except that we use the term protocol instead of interface, because a protocol is more general that what is usually meant by an interface. It follows that slots should be mentioned only in the implementation part of a module.

Consider some call to a protocol function. The call may have some side effects that are internal to the module, and hopefully documented by the module documentation, and it may have some return values. Whether some return value is merely an object stored in some slot of an object that the module manipulates, or whether the module computes that return value, should not be of any importance to the caller. And in fact, the module maintainer may change from one technique to the other as a result of normal module maintenance. And client code should not have to be modified as a result of such a change.

Since client code should not be aware of any slots of the classes known to the module, it follows that client code should never use slot-value or with-slots on an object that is an instance of a class defined by the module. Some people say that this rule is enough. The author of some client code that violates this rule is making a serious mistake. So if a protocol function returns an object that the current implementation stores in a slot, then the protocol function is often a slot reader or slot accessor and the name of it appears in the slot specifier of the class in the module.

The question then is, what does the slot specifier of such a slot look like? If the rule mentioned above is considered enough, then the slot specifier might look like this:

(name :initarg :name :reader name)
A call to the protocol function name would then simply be (name object)

But Common Lisp has a package system in which a package contains symbols that are used as names of many things, including functions, variables, classes, and slots. In the slot specifier shown above, the slot and the slot reader have the same name, and this name must be exported by the package defined by the module, so that the slot reader can be used by client code the normal way, say (module:name object) with a single package marker. A call by client code such as (slot-value object 'module:name) would be a violation of the rule cited above. But some module authors would consider the slot specifier above as being a signal to client code that using slot-value is acceptable since, after all, the name of the slot is exported.

For that reason, many module authors prefer to have different names for the slot itself and for the corresponding slot accessor. The question, then, is whether to rename the slot or the accessor, and how to rename it. It is hard to come up with two entirely different names for a single item like a slot and its accessor, so some systematic naming is called for.

The rule I have started to use myself is to prefix the slot name with the % character. This character is traditionally used to indicate "private" or "danger". The slot specifier above would then instead look like this:

(%name :initarg :name :reader name)
Since the name of the slot is not exported, client code that wants to access the slot using slot-value would have to write (slot-value object module::%name). This form contains two separate indications that rules have been violated, namely the double package marker, and the % character.

Another convention that I sometimes see is to rename the slot accessor instead of the slot itself, and to use the suffix -of. The slot specification above would then instead look like this:

(name :initarg :name :reader name-of)
Only the name of the accessor would be exported, so client code would have to use a double package marker in order to use slot-value. However, here name-of is a protocol function, and it should not be of an concern to client code whether the protocol function computes a value or return the contents of a slot. It then follows that every protocol function must end with -of, or at least the ones that return a value. If not, the -of suffix would reveal that it is a slot accessor. Worse, if the module maintainer decides to instead compute the return value, then the name of the protocol function would have to change, which is contrary to the idea that the protocol is independent of the implementation.

It is interesting to observe that some languages encourage the use of prefixes such as get- and set- for slot accessors (thought the slots are called "fields" or "data members" in such languages). This convention has exactly the same problem as the -of convention mentioned above.


robert.strandh@gmail.com