Defining Classes

Class definition are EuLisp's way of defining new datatypes, but because EuLisp is an object-oriented language, you can build hierarchies of types which inherit structure and behaviour.

A new datatype in EuLisp is called a class. An entity of that class is called an instance. For example, 1 is an instance of the class <integer>. Notice the angle brackets: this is just a syntactic convention in EuLisp; all language-defined class names begin with < and end with >. The class <integer> is an instance of the class <class>, which, in turn, is an instance of itself.

The fields in a class are called slots. Defining a class automatically defines reader and writer functions for each slot.

In the following section we will use the example of defining a rational number class to illustrate the various elements of class definition. The definition of <ratio> could look like this:

(defclass () ((num keyword: num: default: 0 accessor: num) (den keyword: den: default: 1 accessor: den)) predicate: ratiop constructor: (ratio num: den:))

Class definition

The general syntax for defining a class looks like this:

(defclass class-name superclass-name slot-descriptions class-options)

Where the items in italics have the following properties:

class-name
An identifier which names the class. The value of identifier is the new class. In our example, the name of the new class is <ratio>.
superclass-name
An identifier which names the class from which the new class will inherit slots and behaviour (the latter via generic functions). Alternatively, this may be (), indicating <object>, which is what we have chosen in our example.
slot-descriptions
An expression of the form (slot-1 ... slot-n). Each slot-i is of the form ( slot-name keyword: key-name default: expression accessor: identifier). In our example we have ((num keyword: num: default: 0 accessor: num) (den keyword: den: default: 1 accessor: den)) These are explained as follows:
slot-name
A slot-name is an identifier. In our example, num and den.
keyword:
An instance of the class <keyword> used to indicate that the following identifier is to be the keyword used to refer to this slot.
key-name
Although key-name has the same syntax as an identifier, it is in fact an intance of the class <keyword>. By convention, keywords are distinguished by a suffix colon. The keyword will be used in the description of the constructor function or as a keyword argument in a call to make (the general constructor function). In our example, num: and den:.
default:
A keyword used to indicate that the following expression is the one to be evaluated every time a default value is required for this slot. In our example, 0 for the numerator and 1 for the denominator. This means that if we don't supply a numerator and a denominator, we will get back 0/1, which is adequate as a representation of 0.
expression
An expression to to be used to initialize this slot, if a value is not supplied in the call to the constructor.
accessor:
A keyword used to indicate that the following identifer is to be used to name the accessor function for this slot.
identifier
The name of the accessor function, such that (identifier exp), where exp results in an instance of this class, returns the value of this slot, and that ((setter identifier) exp-1 exp-2), where exp-1 results in an instance of this class, updates the slot with the result of exp-2. In our example, num and den, but note that there is no requirement that they be the same as the slot name, it just often makes sense that they are. user> (setq x (ratio 2 3)) # user> (num x) 2 user> (den x) 3 user> ((setter num) x 4) 4 user> x # The above sequence of interactions shows the creation of a a new rational 2/3 and the use of the reader and (via setter) writer functions to operate on it.
class-options
There are three important class options described below. In our example, we have: predicate: ratiop constructor: (ratio num: den:)
abstractp:
A keyword to be followed by a non-nil value to indicate that we are defining an abstract class. Not used in our example. Abstract classes cannot be instantiated. They are used to describe general properties of a class which are then inherited by other classes (sometimes called concrete classes) which can be instantiated. For example, <object> is an abstract class.
constructor:
A keyword to be followed by a constructor function description of the form (id key-1 ... key-n), which will define a function named id which will take n arguments which will be used as the initial values of the slots with those keywords. In our example, we define a constructor function with the name ratio which takes two arguments, the first of which will be the numerator and the second the denominator of the new rational. You can define more than one constructor function for a class, for example: constructor: (int->ratio num:) constructor: (reciprocal den:) Would define a function called int->ratio which takes an integer and returns the corresponding rational and a function called reciprocal which takes an integer and returns 1 upon that integer.
predicate:
A keyword to be followed by an identifier which will name a function which returns true when given an instance of this class and false otherwise. In our example, we define a predicate function named ratiop, following the Lisp convention that functions returning boolean values are suffixed with `p' to indicate predicate.

Julian Padget, jap@maths.bath.ac.uk, this version January 11, 1995