views:

629

answers:

3

I'm trying to learn some Lisp (Common Lisp) lately, and I wonder if there is a way to give constant numbers a name just like you can do in C via enums.

I don't need the full featureset of enums. In the end I just want to have fast and readable code.

I've tried globals and little functions, but that always came with a degration in performance. Just plugging the numbers into the code was always faster.

+5  A: 

Enums are redundant for Lisp, the reason being that all symbols are their own identity, so you can just use those, for example:

[dsm@localhost:~]$ clisp -q
[1]> (setf x 'some) ;'
SOME
[2]> (eq x 'some) ;'
T
[3]>
dsm
+13  A: 

The normal way to do enumerations in Lisp is to use symbols. Symbols get interned (replaced with pointers to their entries in a symbol table) so they are as fast as integers and as readable as enumerated constants in other languages.

So where in C you might write:

enum {
   apple,
   orange,
   banana,
};

In Lisp you can just use 'apple, 'orange and 'banana directly.

If you need an enumerated type, then you can define one with deftype:

(deftype fruit () '(member apple orange banana))

and then you can use the type fruit in declare, typep, typecase and so on, and you can write generic functions that specialize on that type.

Gareth Rees
Yes, but better use keywords.
kmkaplan
That's a good point: by using keyword you avoid having to worry about which package your symbols are in.
Gareth Rees
Actually, generic functions can only specialise on classes, not arbitrary types.
Matthias Benkard
But you can write methods for symbols. (defmethod draw-line (x y (style (eql :dashed))) ...)
Rainer Joswig
What if you want slightly stronger typing? I want to make sure I don't make a typo which won't be detected until runtime? I'm not really a lisp guy, and I know it isn't a strongly typed language, just curious.
Mike Weller
Michael: then you do what you do anywhere -- write a unit test. There will always be typos your compiler can't catch (the sign of an int, inequality operators, string literals, etc.). :-) Really, of all the bugs I've ever had in Lisp code, a vanishingly small fraction were type errors.
Ken
+7  A: 

For example you want to name font sizes:

(defconstant +large+ 3)
(defconstant +medium+ 2)
(defconstant +small+ 1)

You could write a macro to make that shorter.

Above constant definitions are usually written ONLY when these numbers need to be passed to some external non-Lisp code.

Otherwise one would just use keyword symbols: :large, :medium and :small.

You can test them with EQ and everything that uses some test for equality.

(let ((size :medium))
  (ecase size
    (:small ...)
    (:medium ...)
    (:large ...)))

You can also write methods for it:

(defmethod draw-string (message x y (size (eql :large))) ...)

As mentioned you could define a set type:

(deftype size () '(member :small :medium :large))

Then you can check if something is either of those:

(let ((my-size :medium))
  (check-type my-size size))

Above would signal an error if my-size is not one of :small, :medium or :large.

You can also use the type in a defclass form:

(defclass vehicle ()
   ((width :type size :initarg :width)))

Now you would create objects like here:

(make-instance 'vehicle :width :large)

Some Common Lisp implementations will check when you set the slot to some illegal value.

If you now create objects of class vehicle, the slots will be one of :large, :medium or :small. If you look at the object in a debugger, inspector or some other tool you will see the symbolic names and not 1, 2 or 3 (or whatever values you would normally use).

This is part of the Lisp style: use symbolic names when possible. Use symbols with number values only in interface code to foreign functions (like calling external C code that uses enums).

Rainer Joswig