The procedure conskey provides the most general mechanism for creating new record classes (and new vector classes). However there are two syntax words that are generally easier to use.
The defclass construct defined in REF DEFSTRUCT can be used to create other new record classes or new vector classes (see below for vector classes). The syntax word "defclass" is defined in the autoloadable library, on the basis of conskey.
However, for many purposes the older recordclass construct can be used as a simpler mechanism for defining new record classes. ("recordclass" is also a library syntax word defined in terms of "conskey").
Suppose it were necessary to represent points in 3-D by means of four element triples, one for each coordinate of the point and one for the colour. The corresponding recordclass could be defined thus:
recordclass point3D point_x point_y point_z point_colour;The newer syntax for this, using defclass would appear thus:
defclass point3D {point_x, point_y, point_z, point_colour};As mentioned above, this automatically creates a family of new procedures associated with the new recordclass, including conspoint3D, destpoint3D, ispoint3D, point_x, point_y, etc.
An instance of the class can then be created using the procedure conspoint3D and its components can be accessed or changed using the field selector/updater procedures whose names correspond to the field names above. For example, create two points, one at the origin:
vars point1 = conspoint3D(0, 0, 0, "green"), point2 = conspoint3D(10, 10, 10, "red"); ;;; print them out point1 => ** <point3D 0 0 0 green> point2 => ** <point3D 10 10 10 red> point_colour(point2) => ** red "yellow" -> point_colour(point2); point2 => ** <point3D 10 10 10 yellow>
Lists (and vectors, explained below) can be used whenever records are used. However, records have certain advantages in some contexts. Records are are used in preference to lists when:
o Very large numbers of instances are to be created: records normally take less space in memory than the corresponding lists
o Elements of the records need to be accessed and updated rapidly. Accessing or changing the fourth element of a list normally takes longer than accessing or updating the fourth element of a record. That is because with lists it is necessary to "chain" down the list links, whereas the Nth component of a record can be accessed in one go. (Using offsets from the beginning of the record.)
o Run time type-checking is desirable for robustness or debugging. If you attempt to access the fourth element of a list it will work as long as the list has four or more elements, even if something has gone wrong and the wrong sort of list has been found. If you apply a recordclass field accessor to the wrong sort of structure, even if it has the right number of fields, this will produce a run time error message. For example:
front(point1) => ;;; MISHAP - PAIR NEEDED ;;; INVOLVING: <point3D 0 0 0 green> ;;; DOING : sysprmishap mishap front .....Lists are particularly useful when you wish to extend a structure by inserting new items in the middle, or when you wish to have a number of structures with shared "tails", as in these examples:
vars list1 = [a b c], list2 = [d e f], list3, list4; list1 => ** [a b c] ;;; Extend list1 by adding a new item after "a" conspair("item", tl(list1)) -> tl(list1); list1 => ** [a item b c] ;;; Make two new lists that both share list2 as their tail [apple ^^list2] -> list3; [orange ^^list2] -> list4; list3 => ** [apple d e f] list4 => ** [orange d e f] ;;; they have identical tails, sharing memory tl(list3) == tl(list4) => ** <true>That cannot be done with vectors or records.