The following stylistic principle applies to virtually every modern programming language

No Anonymous Constants

An anonymous constant is any unnamed number, string, character, or quoted expression in code. Here are some examples:

The problem is not knowing what the constants are, it's knowing what they mean. An anonymous constant has no name to tell you what it's referring to. Does the constant 12 refer to "number of eggs in a carton," "number of apostles," "number of characters allowed in a last name," or what?

There are two problems with anonymous constants:

The meaning of an anonymous constant has to be inferred from the code context, and that context may require reading the entire program. Furthermore, constants have a way of not staying constant. There are egg cartons now that hold 18 eggs. To fix a program calculating egg prices, written with anonymous constants, we would have to find every occurrence of 12 and look at the context to see if that 12 refers to carton size.

Defining Constants in Lisp

In Common Lisp, we name constants with defconstant, e.g.,

(defconstant carton-size 12
   "Number of eggs in one carton.")

This special form is like defvar except that it tells Lisp that the variable being defined is supposed to be a constant. That means that

Here's how our example code with anonymous constants might look with named constants:

Common Lisp has a number of pre-defined named constants. The two best known are T and NIL. Other constants include pi and lambda-list-keywords.

Unlike global variables defined with defvar and defparameter, constant names are not starred. This is true for built-in constants, such as nil, t Stars (asterisks) are put on global variables to highlight them because

None of these points applies to constants, because they can't be rebound.

Use Short Well-Named Functions Freely

Most novice programmers and many experienced hackers define far too few functions. If you look at my code, even the code I generate on the fly in class, you'll see that I rarely write functions longer than 6 lines of Lisp code. Anything longer gets broken up into subfunctions. Why? Because code appropriately divided into many short functions is:

Of course, these advantages hold only if the parenthetical conditions hold: functions needs to be well-named, single-tasked, and side-effect free.

For a very compatible view of functions from an experienced C++ programmer and manager, see Steve McConnel's Best Practices column, Why You Should use Routines...Routinely in IEEE Software, July/August 1998.

Export Accessor Functions, Not Variables

There are two ways to communicate global information.

In the simplest situations, it's trivial to define a reader and writer given a global variable.

(defvar *line-width* 72
      "Line width for display functions.")

    (defun line-width () *line-width*)

    (defun set-line-width (n)
      (setq *line-width* n))

To enable (setf (line-width) ...) in Common Lisp 1 or 2, add

(defsetf line-width set-line-width)

In Common Lisp 2, you can replace set-line-width and defsetf with

(defun (setf line-width) (n)
  (setq *line-width* n))

10 Reasons Why Accessors are Better than Globals

Global variables should always have names with stars, e.g., *current-color*, to clearly document their special status (pun intended). But starred variables clutter up code badly. [When someone hands experienced programmers a page of code littered with starred variables, their first reaction is "Asterisks! The gall!" (pun intended but obscure)] Accessor functions however are just regular functions and need no such special naming.
Read-only access
Often there is global information that the user should be able to access, but not change. By exporting only a reader function, you can prevent users from changing information that can not or should not be changed.
Write-only access
While less common, sometimes there is information that needs to be specified that should not be readable, by some users at least. (set-password ...) comes to mind, here.
Uniform Access to Non-Variables
There's more to a computer than CPU and memory. There are clocks, I/O ports, and so on. (current-time) and (set-current-time ...) offer an easy to understand way to access such information, despite the internal details.
Localized Values
Consider line width. When we set it to 50 characters, do we mean that to apply to all output, including output to files, or just to output to the screen or to some window on the screen? With accessors, it's easy to extend the calling format to allow values to be attached to local contexts, e.g., (set-line-width stream value).
Safe Assignment
Consider line width again. What happens if someone says (setq *line-width* nil)? Chances are nothing happens until later when printing is attempted, at which point an error occurs. With an accessor like set-line-width, bad values can be caught and prevented at assignment time.
Simplified Assignment
Consider line width once more. With an accessor like set-line-width, we can extend the values it accepts to include things like named standardized values, such as (set-line-width :wide).
Assignment by Example
Consider date formats. There are many ways dates can be printed: full month names vs. abbreviated month names versus digital months, two-digit vs. four-digit years, month-day-year vs. day-month-year, hyphens vs slashes vs. spaces and commas, etc. A clever way of making this complex combination of choices simple to specify is to allow the user to given an example date, e.g., (set-date-format "2/10/95").
Another use of this would be to set a default pathname for files by giving the full pathname for one file, from which the default information could be extracted.
Assignment by Parts
Consider default pathnames, e.g., the default pathname for module binary files. Even though it makes sense to store this internally as one pathname, it makes more sense to allow the user to modify pieces of it without worrying about the other parts, e.g., (set-module-binary-pathname :directory "Lisp:").
Traceable Access
You can't easily trace when someone gets or sets the value of a global variable. No problem with accessor functions.
Faculty: Chris Riesbeck
Time: Monday, Wednesday, Friday: 11:30am - 12:20pm
Location: Zoom link on Canvas


Important Links