It's hard to over-emphasize the importance names bring to the maintainability of code. Unfortunately, there is no formula for generating names, just some good heuristics, and some major "don't"'s.
Bad names reduce code maintainability. Names are bad when they are:
- cryptic, e.g.,
bltx
- hopelessly vague, e.g.,
data
- overly verbose, e.g.,
first-item-to-delete
- misleading, e.g.,
num
for something that holds a list of numbers
Good names are:
- short
- plain English or follow common convention
- accurate
Function Names
A function name should
- say what a function returns or does, e.g.,
get-test
, never how it does it, e.g.,search-recursively
- be either a verb-object, e.g.,
get-test
, or qualified-verb, e.g.,string-sort
- be semantic for application-specific functions, e.g.,
remove-test-case
- be generic for general utilities,
e.g.,
reverse-sequence
Avoid names that are:
- hopelessly vague, such as
get-data
orrun-process
- verbose, such as
get-first-matching-item
; code calling this function will be hard to indent and present readably - misleading, such as
get-square
for a function that actually returns a pair of squares - ambiguous, such as
test-cases
; is this returning a set of test cases or testing some cases?
Common Bad Function Prefixes and Suffixes
Avoid the prefix
check-
as in check-date
. Other than
saying that something is being checked, this is useless. Is this
a predicate? Does it return true for good data? Does it print a warning?
Does it throw an error? Say what you mean, e.g., date-legal-p
,
or report-bad-date
.
Avoid the prefix my-
as in my-length
.
That says nothing about what makes your length
different from the standard one.
Avoid the suffix -helper
as in read-date-helper
.
If there's a subtask in need of a function, there's a name for that subtask.
Sometimes, helpers are created just to get an additional parameter for
recursion. Use optional or keyword parameters instead.
For the same reason, avoid numeric suffixes,
as in read-date-1
and read-date-2
.
A numeric suffix communicates nothing.
(Note: macroexpand-1 is an exception; the suffix means something,
namely "expand once.")
One common reason for difficulty in finding a short accurate name for a function is because the function violates the Cardinal Rule of Functions.
A bit tricky to name are helper functions, e.g., these two common recursive definitions:
(defun fact (n) (fact2 n 1)) (defun fact2 (n r) (if (= n 1) r (fact2 (1- n) (* n r)))) (defun my-reverse (l) (my-reverse-helper l nil)) (defun my-reverse-helper (l r) (if (null l) r (my-reverse-helper (cdr l) (cons (car l) r))))
The code is fine but the names of the helper functions are bad. If all else fails, use a suffix that at least indicates that this is the function that does the looping, e.g., fact-loop. But often the function can be distinguished by how the parameters differ, e.g., a cube-root function might handle a negative number by calling a helper to get the cube root of the absolute value. In that case, call the helper cube-root-positive.
In the case of my-reverse the helper function actually does a different task, even if it's not used that way. The helper returns a list with the reversed elements of l followed by the non-reversed elements of r. This function actually exists in Lisp and is called revappend for "reverse append."
Variable Names
Most of the rules that apply to function names apply to variable names, as well, except that variables hold objects, not actions, so verb-object and qualified-verb are inappropriate name forms.
In addition, when defining functions that are generic and reusable for a wide variety of tasks, such as a "reverse sequence" function or a "find anywhere" function, there are a number of common conventional short variables names that can and should be used:
x
,y
, andz
for variables that can hold any kind of datai
,j
,k
, andn
for variables that hold numbersl
andlst
for variables that hold listsfn
for a variable that holds a function object
Thus, a function that adds a number to every element in a list of numbers could clearly be defined using:
(defun add-number (n l) (mapcar #'(lambda (i) (+ i n)) l))
The n and i suggest numbers and the l suggests list. There's nothing wrong with
(defun add-number (num num-list) (mapcar #'(lambda (num-item) (+ num-item num)) num-list))
but it's more verbose without being any clearer. On the other hand,
(defun add-number (x y) (mapcar #'(lambda (z) (+ x z)) y))
is definitely bad. The variables are too general and easily confused with each other.
For a list of many ways to not name things, check out the naming section of Roedy Green's excellent essay How to Write Unmaintainable Code.