Log in

From encouragement to pitfalls

Yoni Rabkin put together an elisp file of Common Lisp pitfalls. It will display one at random when slime starts up. Here are a few examples:

REMOVE- and DELETE-DUPLICATES keep the _later_ (in the sequence) of two matching items. To keep the earlier items, use :FROM-END T. Remembering that :FROM-END exists may make it easier to remember the default behavior.

READ-FROM-STRING has some optional arguments before the keyword parameters. If you want to supply some keyword arguments, you have to give all of the optional ones too. Other functions with this property: WRITE-STRING, WRITE-LINE, PARSE-NAMESTRING.

Some Common lisp operators use EQ, rather than the usual EQL, in a way that cannot be overridden: CATCH, GET, GET-PROPERTIES, GETF, REMF, REMPROP, and THROW. See table 5-11 on p 5-57 of the standard.

Many of these seem to be about having correct expectations. For example, the wording in the standard is pretty clear that you shouldn't expect a particular initial element in arrays unless you provide one explicitly. For a long time, I expected number-specialized arrays to be initialized to 0 and other arrays to be initialized to NIL. I didn't have a good reason to expect that, it's just what my implementation seemed always to do. I learned the correct expectation after using an implementation that puts random garbage into arrays that aren't explicitly initialized. I thought it might be a bug until I actually read the specification.

Take a look. Can you suggest some more to add?



A quick workaround when using SORT is to use COPY-LIST to create a copy of the input argument you want to pass to sort.

I would never consider writing my own sort function unless the sort feature of a compute intensive application was becoming a performance bottleneck.


This one's weird:

When calling a function on more than two arguments, remember
that there can be intermediate results. For instance, when
adding three fixnums, it's not enough to say only that the
_final_ result is a fixnum. You'll have to break the computation
down into 2-argument calls.


Yeah, that is weird. I'm not sure I understand exactly what he's getting at.

It's talking about using type annotations to speed up the compiler. If you have, for instance,

(defun foo (a b)
  (declare (type fixnum a b))
  (+ a b))

the compiler cannot assume that the result of the addition is a fixnum, for what I hope are obvious reasons. If you have

(defun foo (a b c)
  (declare (type fixnum a b c))
  (+ a b c))

the compiler will probably break that down into pairs of additions, perhaps the equivalent of (+ (+ a b) c). At each step, it has to put in code to check to see if the addition has overflowed into bignums and needs bignum arithmetic. If you tell the compiler that the result will be a fixnum so it needs to do less checking (assuming your speed and safety settings are favorable to such optimization) like this

(defun foo (a b c)
  (declare (type fixnum a b c))
  (the fixnum (+ a b c)))

then that type assertion will only apply to the final addition, and not to any intermediate additions, which may still have additional type-checking code inserted.

This is just something to be aware of when optimizing code, and the rule, of course, is to measure your optimizations to see if you're actually having any effect. It's entirely possible that the compiler will be smart enough to figure out that if the sum of a, b, and c is less than most-positive-fixnum, then so is the sum of just a and b.

Another list


Re: Another list

yrk's elisp file credits Jeff's file.