Classes

Classes in are not static items: they can be defined and created dynamically just as any other type in the system. The following example demonstrates this by defining a class whose instances are themselves classes, whose instances are modular numbers. The intermediate classes are parameterised by an integer, which are the bases for the modular rings. This also illustrates the use of metaclasses, which control the structure of classes.

We create a metaclass <zmodn> which is the class of the classes <Zmod3>, <Zmod5>, <Zmod7>, etc. (defclass <zmodn-class> (<class>) ((n initarg n reader zmodn-class-n)) metaclass <class>)

This will be a direct subclass of class, and so will inherit its methods, in particular the ability to create subclasses which are themselves classes. The instances of this class will have a slot named n, which will be the modular base.

Now we define a superclass for all of its instances, to place them in their own sub-hierarchy of the class graph. This class has an instance variable z, since the instances of its subclasses are the fully instantiated modular numbers. (defclass <zmodn-object> (<number>) ((z accessor zmodn-z)) metaclass <zmodn-class>) The metaclass of the instances of zmodn-object is defined to be the class zmodn-class. Thus the structure of the instances (the classes Zmod5, etc.) is determined by zmodn-class.

The constructor for the instances of zmodn-class (the metaclass) could be the following: (defun make-zmodn-class (n) (make <zmodn-class> 'direct-superclasses (list <zmodn-object>) 'name (make-symbol (format nil "<zmod- a>" n)) 'n n)) The make-instance requires values for the slots in zmodn-class, which include n (the slot we defined), and direct-superclasses, a slot inherited from class.

If you want to avoid creating duplicate zmodn classes with the same N, try this definition instead:

(defconstant *zmodn-table* (make <table> 'comparator = 'hash-function generic-hash))

(defun make-zmodn-class2 (n) (or (table-ref *zmodn-table* n) (let ((cl (make-zmodn-class n))) ((setter table-ref) *zmodn-table* n cl) cl)))

The function to create the modular objects themselves could be defined as follows: (defun make-modular-number (z n) (make-instance (make-zmodn-class2 n) 'z z)) Note that this implemenatation guarentees that the number is of the appropriate range: (defmethod initialize ((proto <zmodn-object>) lst) (let ((i (call-next-method))) ((setter zmodn-z) i (remainder (scan-args 'z lst required-argument) (zmodn-n i))) i))

Getting z from one of these instances is already defined by the reader on zmodn-object. Getting n involves going to the class. Making this available from instances means defining the following function: (defgeneric zmodn-n (obj))

(defmethod zmodn-n ((z <zmodn-object>)) (zmodn-class-n (class-of z)))

Next, we want to define some simple arithmetic on modular numbers, for example, addition. However, this only makes sense if we have the same modulus in both of the summands. (defun compatible-moduli (n m) (if (= (zmodn-n n) (zmodn-n m)) t (error "incompatible moduli" Internal-Error)))

We define a method for addition on <zmodn-object>: this will then be inherited by each instance, viz., the actual rings <zmod3>, <zmod5>, and so on. (defmethod binary-plus ((n1 <zmodn-object>) (n2 <zmodn-object>)) (when (compatible-moduli n1 n2) (make-modular-number (+ (zmodn-z i) (zmodn-z j)) (zmodn-n i))))

We can add a method to the print function to view numbers prettily (defmethod generic-prin ((n <zmodn-object>) s) (format s " a<mod a>" (zmodn-z n) (zmodn-n n)))

Finally, some examples of numbers (deflocal zero5 (make-modular-number 0 5)) (deflocal one5 (make-modular-number 1 5)) (deflocal two5 (make-modular-number 2 5)) (deflocal three5 (make-modular-number 3 5)) (deflocal four5 (make-modular-number 4 5)) (deflocal zero3 (make-modular-number 0 3)) (deflocal one3 (make-modular-number 1 3)) (deflocal two3 (make-modular-number 2 3))

Now if we try an addition:

> (+ two5 four5)
< 1<mod 5>
We didn't have to specify a plus method for each modular ring individually: the single definition on the superclass suffices.

Thanks to Harley Davis for help on this section.