{Begin Chapter Record Package}
{Title Record Package}
{Text


{note The record package was written by L. M. Masinter.}


{Tag RecordPackage}

{index *PRIMARY* Record package}


The advantages of "data abstraction" have long been known: more readable code, fewer bugs, the ability to change the data structure without having to make major modifications to the program, etc.  The record package encourages and facilitates this good programming practice by providing a uniform syntax for creating, accessing and storing data into many different types of data structures (arrays, list structures, association lists, etc.) as well as removing from the user the task of writing the various manipulation routines.  The user declares (once) the data structures used by his programs, and thereafter indicates the manipulations of the data in a data-structure-independent manner.  Using the declarations, the record package automatically computes the corresponding Interlisp expressions necessary to accomplish the indicated access/storage operations.  If the data structure is changed by modifying the declarations, the programs automatically adjust to the new conventions.


The user describes the format of a data structure (record) by making a "record declaration" (see {PageRef Term Record Declarations}).  The record declaration is a description of the record, associating names with its various parts, or "fields".  For example, the record declaration {lisp (RECORD MSG (FROM TO . TEXT))} describes a data structure called {lisp MSG}, which contains three fields: {lisp FROM}, {lisp TO}, and {lisp TEXT}.  The user can reference these fields by name, to retrieve their values or to store new values into them, by using the {lisp FETCH} and {lisp REPLACE} operators ({PageRef (Record Operator) FETCH}).  The {lisp CREATE} operator ({PageRef (Record Operator) CREATE}) is used for creating new instances of a record, and {lisp TYPE?} ({PageRef (Record Operator) TYPE?}) is used for testing whether an object is an instance of a particular record.  (note: all record operators can be in either upper or lower case.)


Records may be implemented in a variety of different ways, as determined by the first element ("record type") of the record declaration.  {lisp RECORD} (used to specify elements and tails of a list structure) is just one of several record types currently implemented.  The user can specify a property list format by using the record type {lisp PROPRECORD}, or that fields are to be associated with parts of a data structure via a specified hash array by using the record type {lisp HASHLINK}, or that an entirely new data type be allocated (as described on {PageRef Term User Data Types}) by using the record-type {lisp DATATYPE}.


The record package is implemented through the DWIM/CLISP facilities, so it contains features such as spelling correction on field names, record types, etc.  Record operations are translated using all CLISP declarations in effect (standard/fast/undoable); it is also possible to declare local record declarations that override global ones (see {PageRef Term CLISP Declarations}).


The file package includes a {filecom RECORDS}{index RECORDS FileCom} file package command for dumping record declarations ({PageRef FileCom RECORDS}), and {fn FILES?} and {fn CLEANUP} will inform the user about records that need to be dumped.



{Begin SubSec FETCH and REPLACE}
{Title FETCH and REPLACE}
{Text

{index *PRIMARY* FETCH (Record Operator)}
{index *PRIMARY* REPLACE (Record Operator)}

The fields of a record are accessed and changed with the {lisp FETCH} and {lisp REPLACE} operators.  If the record {lisp MSG} has the record declaration {lisp (RECORD MSG (FROM TO . TEXT))}, and {lisp X} is a {lisp MSG} data structure, {lisp (fetch FROM of X)} will return the value of the {lisp FROM} field of {lisp X}, and {lisp (replace FROM of X with Y)} will replace this field with the value of {lisp Y}.  In general, the value of a {lisp REPLACE} operation is the same as the value stored into the field.

{note exceptions: via ACCESSFNS, users specify functional forms to do fetching and replacing --- it is up to the user to make sure that replace form returns the value stored in the field}


Note that the form {lisp (fetch FROM of X)} implicitly states that {lisp X} is an instance of the record {lisp MSG}, or at least it should to be treated as such for this particular operation.  In other words, the interpretation of {lisp (fetch FROM of X)} never depends on the value of {lisp X}.  Therefore, if {lisp X} is not a {lisp MSG} record, this may produce incorrect results.  The {lisp TYPE?} record operation ({PageRef (Record Operator) TYPE?}) may be used to test the types of objects.


If there is another record declaration, {lisp (RECORD REPLY (TEXT . RESPONSE))}, then {lisp (fetch TEXT of X)} is ambiguous, because {lisp X} could be either a {lisp MSG} or a {lisp REPLY} record.  In this case, an error will occur, {lisp AMBIGUOUS RECORD FIELD}.{index AMBIGUOUS RECORD FIELD Error}  To clarify this, {lisp FETCH} and {lisp REPLACE} can take a list for their "field" argument:  {lisp (fetch (MSG TEXT) of X)} will fetch the {lisp TEXT} field of an {lisp MSG} record.  Note that if a field has an {it identical} interpretation in two declarations, e.g. if the field {lisp TEXT} occurred in the same location within the declarations of {lisp MSG} and {lisp REPLY}, then {lisp (fetch TEXT of X)} would {it not} be considered ambiguous.

An exception to this rule is that "user" record declarations take precedence over "system" record declarations, in cases where an unqualified field name would be considered ambiguous.  System records are declared by including {lisp (SYSTEM)} in the record declaration (see {PageRef (In Record Declarations) SYSTEM}).  All of the records defined in the standard Interlisp-D system are defined as system records.

Another complication can occur if the fields of a record are themselves records.  The fields of a record can be further broken down into sub-fields by a "subdeclaration" within the record declaration (see {PageRef Term SubDeclarations}).  For example,

{lispcode
(RECORD NODE (POSITION . LABEL) (RECORD POSITION (XLOC . YLOC)))}

permits the user to access the {lisp POSITION} field with {lisp (fetch POSITION of X)}, or its subfield {lisp XLOC} with {lisp (fetch XLOC of X)}.


The user may also elaborate a field by declaring that field name in a {it separate} record declaration (as opposed to an embedded subdeclaration).  For instance, the {lisp TEXT} field in the {lisp MSG} and {lisp REPLY} records above may be subdivided with the seperate record declaration {lisp (RECORD TEXT (HEADER . TXT))}.  Fields of subfields (to any level of nested subfields) are accessed by specifying the "data path" as a list of record/field names, where there is some path from each record to the next in the list.  For instance, {lisp (fetch (MSG TEXT HEADER) of X)} indicates that {lisp X} is to be treated as a {lisp MSG} record, its {lisp TEXT} field should be accessed, and {it its} {lisp HEADER} field should be accessed.  Only as much of the data path as is necessary to disambiguate it needs to be specified.  In this case, {lisp (fetch (MSG HEADER) of X)} is sufficient.  The record package interprets a data path by performing a tree search among all current record declarations for a path from each name to the next, considering first local declarations ({PageRef Term Local record declarations}) and then global ones.  The central point of separate declarations is that the (sub)record is {it not} tied to another record (as with embedded declarations), and therefore can be used in many different contexts.  If a data-path rather than a single field is ambiguous, (e.g., if there were yet another declaration {lisp (RECORD TO (NAME . HEADER))} and the user specified {lisp (fetch (MSG HEADER) of X)}), the error {lisp AMBIGUOUS DATA PATH}{index AMBIGUOUS DATA PATH Error} is generated.


{lisp FETCH} and {lisp REPLACE} forms are translated using the CLISP declarations in effect (see {PageRef Term CLISP Declarations}).  {lisp FFETCH}{index FFETCH (Record Operator)} and {lisp FREPLACE}{index FREPLACE (Record Operator)} are versions which insure fast CLISP declarations will be in effect, {indexx {Name REPLACE} {Type Record Operator} {Text {lisp /REPLACE}} }{index /REPLACE (Record Operator)}{lisp /REPLACE} insures undoable declarations.
 

}{End SubSec FETCH and REPLACE}




{Begin SubSec CREATE}
{Title CREATE}
{Text

{index *PRIMARY* CREATE (Record Operator)}

Record operations can be applied to arbitrary structures, i.e., the user can explicitely creating a data structure (using {fn CONS}, etc), and then manipulate it with {lisp FETCH} and {lisp REPLACE}.  However, to be consistant with the idea of data abstraction, new data should be created using the same declarations that define its data paths.  This can be done with an expression of the form:

{lispcode (create {arg RECORD-NAME} . {arg ASSIGNMENTS})}

A {lisp CREATE} expression translates into an appropriate Interlisp form using {fn CONS}, {fn LIST}, {fn PUTHASH}, {fn ARRAY}, etc., that creates the new datum with the various fields initialized to the appropriate values.  {arg ASSIGNMENTS} is optional and may contain expressions of the following form:


{Begin LabeledList ASSIGNMENTS}

{Label {arg FIELD-NAME} ← {arg FORM}}
{Item
Specifies initial value for {arg FIELD-NAME}.
}


{Label {index USING (in CREATE form)}{lisp USING {arg FORM}}}
{Item
Specifies that for all fields not explicitly given a value, the value of the corresponding field in {arg FORM} is to be used.
}


{Label {index COPYING (in CREATE form)}{lisp COPYING {arg FORM}}}
{Item
Similar to {lisp USING} except the corresponding values are copied (with {fn COPYALL}).
}


{Label {index REUSING (in CREATE form)}{lisp REUSING {arg FORM}}}
{Item
Similar to {lisp USING}, except that wherever possible, the corresponding {it structure} in {arg FORM} is used.
}


{Label {index SMASHING (in CREATE form)}{lisp SMASHING {arg FORM}}}
{Item
A new instance of the record is not created at all; rather, the value of {arg FORM} is used and smashed.
}

{End LabeledList ASSIGNMENTS}


The record package goes to great pains to insure that the order of evaluation in the translation is the same as that given in the original {lisp CREATE} expression if the side effects of one expression might affect the evaluation of another.  For example, given the declaration {lisp (RECORD CONS (CAR . CDR))}, the expression {lisp (create CONS CDR←X CAR←Y)} will translate to {lisp (CONS Y X)}, but {lisp (create CONS CDR←(FOO) CAR←(FIE))} will translate to {lisp ((LAMBDA ($$1) (CONS (PROGN (SETQ $$1 (FOO)) (FIE)) $$1)))} because {lisp FOO} might set some variables used by {lisp FIE}.




Note that {lisp (create {arg RECORD} REUSING {arg FORM} ...)} does not itself do any destructive operations on the value of {arg FORM}.  The distinction between {lisp USING} and {lisp REUSING} is that {lisp (create {arg RECORD} reusing {arg FORM} ...)} will incorporate as much as possible of the old data structure into the new one being created, while {lisp (create {arg RECORD} using {arg FORM} ...)} will create a completely new data structure, with only the contents of the fields re-used.  For example, {lisp REUSING} a {lisp PROPRECORD} just {fn CONS}es the new property names and values onto the list, while {lisp USING} copies the top level of the list.  Another example of this distinction occurs when a field is elaborated by a subdeclaration ({PageRef Term Subdeclarations}): {lisp USING} will create a new instance of the sub-record, while {lisp REUSING} will use the old contents of the field (unless some field of the subdeclaration is assigned in the {lisp CREATE} expression.)


If the value of a field is neither explicitly specified, nor implicitly specified via {lisp USING}, {lisp COPYING} or {lisp REUSING}, the default value in the declaration is used, if any, otherwise {lisp NIL}.  (Note: For {lisp BETWEEN} fields in {lisp DATATYPE} records, {arg N{sub 1}} is used; for other non-pointer fields zero is used.)  For example, following {lisp (RECORD A (B C D) D ← 3)},

{lispcode (create A B←T)
       ==>  (LIST T NIL 3)}

{lispcode (create A B←T using X)
       ==>  (LIST T (CADR X) (CADDR X))}

{lispcode (create A B←T copying X))
       ==>  [LIST T (COPYALL (CADR X)) (COPYALL (CADDR X]}

{lispcode (create A B←T reusing X)
       ==>  (CONS T (CDR X))}



}{End SubSec CREATE}





{Begin SubSec TYPE?}
{Title TYPE?}
{Text

{index *PRIMARY* TYPE? (Record Operator)}

The record package allows the user to test if a given datum "looks like" an instance of a record.  This can be done via an expression of the form

{lispcode (type? {arg RECORD-NAME} {arg FORM})}

{lisp TYPE?} is mainly intended for records with a record type of {lisp DATATYPE} or {lisp TYPERECORD}.  For {lisp DATATYPE}s, the {lisp TYPE?} check is exact; i.e. the {lisp TYPE?} expression will return non-{lisp NIL} only if the value of {arg FORM} is an instance of the record named by {arg RECORD-NAME}.  For {lisp TYPERECORD}s, the {lisp TYPE?} expression will check that the value of {arg FORM} is a list beginning with {arg RECORD-NAME}.  For {lisp ARRAYRECORD}s, it checks that the value is an array of the correct size.  For {lisp PROPRECORD}s and {lisp ASSOCRECORD}s, a {lisp TYPE?} expression will make sure that the value of {arg FORM} is a property/association list with property names among the field-names of the declaration.


There is no built-in type test for records of type {lisp ACCESSFNS}, {lisp HASHLINK} or {lisp RECORD}.  Type tests can be defined for these kinds of records, or redefined for the other kinds, by including an expression of the form {lisp (TYPE? {arg COM})} in the record declaration (see {PageRef Tag RecordDeclarationTYPE?}).  Attempting to execute a {lisp TYPE?} expression for a record that has no type test causes an error, {lisp TYPE? NOT IMPLEMENTED FOR THIS RECORD}.{index TYPE? NOT IMPLEMENTED FOR THIS RECORD Error}

}{End SubSec TYPE?}




{Begin SubSec WITH}
{Title WITH}
{Text

{index *PRIMARY* WITH (Record Operator)}


Often one wants to write a complex expression that manipulates several fields of a single record.  The {lisp WITH} construct can make it easier to write such expressions by allowing one to refer to the fields of a record as if they were variables within a lexical scope:

{lispcode (with {arg RECORD-NAME} {arg RECORD-INSTANCE} {arg FORM{sub 1}} {ellipsis} {arg FORM{sub N}})}

{arg RECORD-NAME} is the name of a record, and {arg RECORD-INSTANCE} is an expression which evaluates to an instance of that record.  The expressions {arg FORM{sub 1}} {ellipsis} {arg FORM{sub N}} are evaluated so that references to variables which are field-names of {arg RECORD-NAME} are implemented via {lisp FETCH} and {fn SETQ}s of those variables are implemented via {lisp REPLACE}. 

For example, given

{lispcode
(RECORD RECN (FLD1 FLD2))
(SETQ INST (create RECN FLD1 ← 10 FLD2 ← 20))}

Then the construct

{lispcode
(with RECN INST (SETQ FLD2 (PLUS FLD1 FLD2]}

is equivalent to

{lispcode
(replace FLD2 of INST with (PLUS (fetch FLD1 of INST) (fetch FLD2 of INST]}

Warning:  {lisp WITH} is implemented by doing simple substitutions in the body of the
forms, without regard for how the record fields are used.  This means, for example, if the record {lisp FOO} is defined by {lisp (RECORD FOO (POINTER1 POINTER2))}, then the form

{lispcode (with FOO X (SELECTQ Y (POINTER1 POINTER1) NIL]}

will be translated as

{lispcode (SELECTQ Y ((CAR X) (CAR X)) NIL]}

The user should be careful that record field names are not used except as variables in the {lisp WITH} forms.

}{End SubSec WITH}





{Begin SubSec Record Declarations}
{Title Record Declarations}
{Text


{Tag RecordDeclarations}

{index *PRIMARY* Record declarations}


A record is defined by evaluating a record declaration, which is an expression of the form:

{lispcode
({arg RECORD-TYPE} {arg RECORD-NAME} {arg RECORD-FIELDS} . {arg RECORD-TAIL})}

{arg RECORD-TYPE}{index Record types} specifies the "type" of data being described by the record declaration, and thereby implicitly specifies how the corresponding access/storage operations are performed.  The different record types are described below.

{arg RECORD-NAME} is a litatom used to identify the record declaration for creating instances of the record via {lisp CREATE}, testing via {lisp TYPE?}, and dumping to files via the {filecom RECORDS} file package command ({PageRef FileCom RECORDS}).  {lisp DATATYPE} and {lisp TYPERECORD} declarations also use {arg RECORD-NAME} to identify the data structure (as described below).

{arg RECORD-FIELDS} describes the structure of the record.  Its exact interpretation varies with {arg RECORD-TYPE}.  For most record types it defines the names of the fields within the record that can be accessed with {lisp FETCH} and {lisp REPLACE}.

{arg RECORD-TAIL} is an optional list that can be used to specify default values for record fields, special {lisp CREATE} and {lisp TYPE?} forms, and subdeclarations (described below).


{index Local record declarations}

Normally, record declaration forms are typed in to the top-level executive or read from a file, and they define the structure of the record globally.  Local record declarations within the context of a function are defined by including a record declaration form in the CLISP declaration for the function, rather than evaluating the expression itself (see {PageRef Term Local record declarations}).


Note:  Although record declarations are evaluatable forms, and thus can be included in functions, changing a record declaration dynamically (at run-time) is not recommended.  When a {lisp FETCH} or {lisp REPLACE} operation is interpreted, and the record declaration has changed, the form has to be re-translated.  If a function containing {lisp FETCH} or {lisp REPLACE} operations has been compiled, it may be necessary to re-compile.  For applications which need to change record declarations dynamically, users should consider using more flexible data structures, such as association lists or property lists. 

{Begin Note}
Why would anyone ever want to do this??

For some top-level declarations, RECORD-NAME is optional, e.g., (RECORD (ID (FROM TO) . TEXT)) is acceptable.  However, if RECORD-NAME is omitted, the user cannot specify the record by name, e.g., in CREATE expressions, or when using the RECORDS file package command.
{End Note}




{Begin SubSec Record Types}
{Title Record Types}
{Text

{Index *PRIMARY* Record types}

Records can be used to describe a large variety of data objects, that are manipulated in different ways.  The {arg RECORD-TYPE} field of the record declaration specifies how the data object is created, and how the various record fields are accessed.  Depending on the record type, the record fields may be stored in a list, or in an array, or on the property list of a litatom.  The following record types are defined:



{Def {Type (Record Type)} {Name RECORD}
{Text
The {lisp RECORD} record type is used to describe list structures.  {arg RECORD-FIELDS} is interpreted as a list structure whose non-{lisp NIL} literal atoms are taken as field-names to be associated with the corresponding elements and tails of a list structure.  For example, with the record declaration {lisp (RECORD MSG (FROM TO . TEXT))}, {lisp (fetch FROM of X)} translates as {lisp (CAR X)}.  

{lisp NIL} can be used as a place marker to fill an unnamed field, e.g., {lisp (A NIL B)} describes a three element list, with {lisp B} corresponding to the third element.  A number may be used to indicate a sequence of {lisp NIL}s, e.g. {lisp (A 4 B)} is interpreted as {lisp (A NIL NIL NIL NIL B)}.
}}



{Def {Type (Record Type)} {Name TYPERECORD}
{Text
The {lisp TYPERECORD} record type is similar to {lisp RECORD}, except that the record name is added to the front of the list structure to signify what "type" of record it is.  This type field is used by the record package in the translation of {lisp TYPE?}{index TYPE? (Record Operator)} expressions.  {lisp CREATE} will insert an extra field containing {arg RECORD-NAME} at the beginning of the structure, and the translation of the access and storage functions will take this extra field into account.  For example, for {lisp (TYPERECORD MSG (FROM TO . TEXT))}, {lisp (fetch FROM of X)} translates as {lisp (CADR X)}, not {lisp (CAR X)}.
}}



{Def {Type (Record Type)} {Name ASSOCRECORD}
{Text
The {lisp ASSOCRECORD} record type is used to describe list structures where the fields are stored in association list format:

{lispcode
(({arg FIELDNAME{sub 1}} . {arg VALUE{sub 1}}) ({arg FIELDNAME{sub 2}} . {arg VALUE{sub 2}}) {ellipsis})}


{arg RECORD-FIELDS} is a list of literal atoms, interpreted as the permissable list of field names in the association list.  Accessing is performed with {fn ASSOC} (or {fn FASSOC}, depending on current {lisp CLISP} declarations, see {PageRef Term CLISP Declarations}), storing with {fn PUTASSOC}.
}}



{Def {Type (Record Type)} {Name PROPRECORD}
{Text
The {lisp PROPRECORD} record type is used to describe list structures where the fields are stored in property list format:

{lispcode
({arg FIELDNAME{sub 1}} {arg VALUE{sub 1}} {arg FIELDNAME{sub 2}} {arg VALUE{sub 2}} {ellipsis})}

{arg RECORD-FIELDS} is a list of literal atoms, interpreted as the permissable list of field names in the property list.  Accessing is performed with {fn LISTGET}, storing with {fn LISTPUT}.
}}



Both {lisp ASSOCRECORD} and {lisp PROPRECORD} are useful for defining data structures in which it is often the case that many of the fields are {lisp NIL}.  A {lisp CREATE} expression for these record types only stores those fields which are non-{lisp NIL}.  Note, however, that with the record declaration {lisp (PROPRECORD FIE (H I J))} the expression {lisp (create FIE)} would still construct {lisp (H NIL)}, since a later operation of {lisp (replace J of X with Y)} could not possibly change the instance of the record if it were {lisp NIL}.



{Def {Type (Record Type)} {Name ARRAYRECORD}
{Text
The {lisp ARRAYRECORD} record type is used to describe arrays.  {arg RECORD-FIELDS} is interpreted as a list of field names that are associated with the corresponding elements of an array.  {lisp NIL} can be used as a place marker for an unnamed field (element).  Positive integers can be used as abbreviation for the corresponding number of {lisp NIL}s.  For example, {lisp (ARRAYRECORD (ORG DEST NIL ID 3 TEXT))} describes an eight element array, with {lisp ORG} corresponding to the first element, {lisp ID} to the fourth, and {lisp TEXT} to the eighth.

Note that {lisp ARRAYRECORD} only creates arrays of pointers.  Other kinds of arrays must be implemented by the user with the {lisp ACCESSFNS} record type ({PageRef (Record Type) ACCESSFNS}).
}}



{Def {Type (Record Type)} {Name HASHLINK}
{Text
The {lisp HASHLINK} record type can be used with any type of data object: it specifies that the value of a single field can be accessed by hashing the data object in a given hash array.  Since the {lisp HASHLINK} record type describes an accessing method, rather than a data structure, the {lisp CREATE} expression is meaningless for {lisp HASHLINK} records.

{arg RECORD-FIELDS} is either an atom {arg FIELD-NAME}, or a list {lisp ({arg FIELD-NAME} {arg HARRAYNAME} {arg HARRAYSIZE})}.  {arg HARRAYNAME} is a variable whose value is the hash array to be used; if not given, {var SYSHASHARRAY} is used.  If the value of the variable {arg HARRAYNAME} is not a hash array (at the time of the record declaration), it will be set to a new hash array with a size of {arg HARRAYSIZE}.  {arg HARRAYSIZE} defaults to 100.

The {lisp HASHLINK} record type is useful as a subdeclaration to other records to add additional fields to already existing data structures (see {PageRef Term Subdeclarations}).  For example, suppose that {lisp FOO} is a record declared with {lisp (RECORD FOO (A B C))}.  To add an aditional field {lisp BAR}, without modifying the already existing data strutures, redeclare {lisp FOO} with:

{lispcode (RECORD FOO (A B C) (HASHLINK FOO (BAR BARHARRAY)))}

Now, {lisp (fetch BAR of X)} will translate into {lisp (GETHASH X BARHARRAY)}, hashing off the existing list {lisp X}.


{note HASHLINK may also be called HASHRECORD (HASHLINK defined as USERRECORDTYPE)}
}}



{Def {Type (Record Type)} {Name ATOMRECORD}
{Text
The {lisp ATOMRECORD} record type is used to describe property lists of litatoms.  {arg RECORD-FIELDS} is a list of property names.  Accessing is performed with {fn GETPROP}, storing with {fn PUTPROP}.  The {lisp CREATE} expression is not initially defined for {lisp ATOMRECORD} records.
}}


{Def {Type (Record Type)} {Name DATATYPE}
{Text
The {lisp DATATYPE} record type is used to define a new user data type with type name {arg RECORD-NAME} (by calling {fn DECLAREDATATYPE}, {PageRef Fn DECLAREDATATYPE}).  Unlike other record types, the records of a {lisp DATATYPE} declaration are represented with a completely new Interlisp type, and not in terms of other existing types.  

{arg RECORD-FIELDS} is interpreted as a list of field specifications, where each specification is either a list {lisp ({arg FIELDNAME} {arg FIELDTYPE})}, or an atom {arg FIELDNAME}.  If {arg FIELDTYPE} is omitted, it defaults to {lisp POINTER}.  Possible values for {arg FIELDTYPE} are:

{Begin LabeledList Other options for FIELDTYPE}

{Label {lisp POINTER}{index POINTER (record field type)}}
{Text
Field contains a pointer to any arbitrary Interlisp object.
}

{Label {lisp INTEGER}{index INTEGER (record field type)}}
{Label {lisp FIXP}{index FIXP (record field type)}}
{Text
Field contains a signed integer.  Note that an {lisp INTEGER} field is not capable of holding everything that satisfies {fn FIXP}, such as bignums ({PageRef Term Bignums}).
}

{Label {lisp FLOATING}{index FLOATING (record field type)}}
{Label {lisp FLOATP}{index FLOATP (record field type)}}
{Text
Field contains a floating point number.
}

{Label {lisp SIGNEDWORD}{index SIGNEDWORD (record field type)}}
{Item
Field contains a 16-bit signed integer.
}

{Label {lisp FLAG}{index FLAG (record field type)}}
{Text
Field is a one bit field that "contains" {lisp T} or {lisp NIL}.
}

{Label {lisp BITS {arg N}}{index BITS (record field type)}}
{Text
Field contains an {arg N}-bit unsigned integer.
}

{Label {lisp BYTE}{index BYTE (record field type)}}
{Item
Equivalent to {lisp BITS 8}.
}

{Label {lisp WORD}{index WORD (record field type)}}
{Item
Equivalent to {lisp BITS 16}.
}

{Label {lisp XPOINTER}{index XPOINTER (record field type)}}
{Item
Field contains a pointer like {lisp POINTER}, except that the field is {it not} reference counted by the Interlisp-D garbage collector.  {lisp XPOINTER} fields are useful for implementing back-pointers in structures that would be circular and not otherwise collected by the reference-counting garbage collector.

Warning: {lisp XPOINTER} fields should be used with great care.  It is possible to damage the integrity of the storage allocation system by using pointers to objects that have been garbage collected.  Code that uses {lisp XPOINTER} fields should be sure that the objects pointed to have not been garbage collected.  This can be done in two ways:  The first is to maintain the object in a global structure, so that it is never garbage collected until explicitly deleted from the structure, at which point the program must invalidate all the {lisp XPOINTER} fields of other objects pointing at it.  The second is to declare the object as a {lisp DATATYPE} beginning with a {lisp POINTER} field that the program maintains as a pointer to an object of another type (e.g., the object containing the {lisp XPOINTER} pointing back at it), and test that field for reasonableness whenever using the contents of the {lisp XPOINTER} field.
}

{End LabeledList Other options for FIELDTYPE}


For example, the declaration

{lispcode
(DATATYPE FOO
   ((FLG BITS 12)
    TEXT
    HEAD
    (DATE BITS 18)
    (PRIO FLOATP)
    (READ? FLAG)))}

would define a data type {lisp FOO} with two pointer fields, a floating point number, and fields for a 12 and 18 bit unsigned integers, and a flag (one bit).  Fields are allocated in such a way as to optimize the storage used and not necessarily in the order specified. Generally, a {lisp DATATYPE} record is much more storage compact than the corresponding {lisp RECORD} structure would be; in addition, access is faster.

Since the user data type must be set up at {it run}-time, the {filecom RECORDS}{index RECORDS FileCom} file package command will dump a {fn DECLAREDATATYPE} expression as well as the {lisp DATATYPE} declaration itself.  If the record declaration is otherwise not needed at runtime, it can be kept out of the compiled file by using a {lisp (DECLARE: DONTCOPY --)} expression (see {PageRef Filecom DECLARE:}), but it is still necessary to ensure that the datatype is properly initialized.  For this, one can use the {filecom INITRECORDS}{index INITRECORDS FileCom} file package command ({PageRef FileCom INITRECORDS}), which will dump only the {fn DECLAREDATATYPE} expression.


Note:  When defining a new data type, it is sometimes useful to call the function {fn DEFPRINT} ({PageRef Fn DEFPRINT}) to specify how instances of the new data type should be printed.  This can be specified in the record declaration by including an {lisp INIT} record specification ({PageRef (In Record Declarations) INIT}), e.g. {lisp (DATATYPE QV.TYPE {ellipsis} (INIT (DEFPRINT 'QV.TYPE (FUNCTION PRINT.QV.TYPE))))}.


Note: {lisp DATATYPE} declarations cannot be used within {index Local record declarations}local record declarations ({PageRef Term Local record declarations}).


{Begin Note}
should document somewhere (\SIGNED bits width) which returns a signed integer from an unsigned field, and (\UNSIGNED n width) which (using LOGAND) turns a signed integer into a width-bit field.
{End Note}

{note Need to document in DATATYPE record (or somewhere, with crossrefs.) the size information for FIXP vs. WORD vs. POINTER etc. so that users like me can make intelligent choices about how to reduce their storage usage.}
}}


{Def {Type (Record Type)} {Name BLOCKRECORD}
{Text
The {lisp BLOCKRECORD} record type is used in low-level system programming to "overlay" an organized structure over an arbitrary piece of "unboxed" storage.  {arg RECORD-FIELDS} is interpreted exactly as with a {lisp DATATYPE} declaration, except that fields are {it not} automatically rearranged to maximize storage efficiency.  Like an {lisp ACCESSFNS} record, a {lisp BLOCKRECORD} does not have concrete instances; it merely provides a way of interpreting some existing block of storage.  Thus, one cannot create an instance of a {lisp BLOCKRECORD} (unless the declaration includes an explicit {lisp CREATE} expression), nor is there a default {lisp type?} expression for a {lisp BLOCKRECORD}.

Warning: The programmer should exercise caution in using {lisp BLOCKRECORD} declarations, as they enable one to write expressions that fetch and store arbitrary data in arbitrary locations, thereby evading the normal type system.  Except in very specialized situations, a {lisp BLOCKRECORD} should never contain {lisp POINTER} or {lisp XPOINTER} fields, nor be used to overlay an area of storage that contains pointers.  Such use could compromise the garbage collector and storage allocation system.  The programmer is responsible for ensuring that all {lisp FETCH} and {lisp REPLACE} expressions are performed only on suitable objects, as no type testing is performed.

A typical use for the {lisp BLOCKRECORD} type in user code is to overlay a non-pointer portion of an existing {lisp DATATYPE}.  For this use, the {index LOCF (Macro)}{lisp LOCF} macro is useful.  {lisp (LOCF (fetch {arg FIELD} of {arg DATUM}))} can be used to refer to the storage that begins at the first word that contains {arg FIELD} of {arg DATUM}.  For example, to define a new kind of Ethernet packet ({PageRef (data type) ETHERPACKET}), one could overlay the "body" portion of the {lisp ETHERPACKET} datatype declaration as follows:

{lispcode
(ACCESSFNS MYPACKET
     ((MYBASE (LOCF (fetch (ETHERPACKET EPBODY) of DATUM))))
	(BLOCKRECORD MYBASE
          ((MYTYPE WORD)
           (MYLENGTH WORD)
           (MYSTATUS BYTE)
           (MYERRORCODE BYTE)
           (MYDATA INTEGER)))
     (TYPE? (type? ETHERPACKET DATUM)))}

With this declaration in effect, the expression {lisp (fetch MYLENGTH of PACKET)} would retrieve the second 16-bit field beyond the offset inside {lisp PACKET} where the {lisp EPBODY} field starts.  For more examples, see the EtherRecords library package.
}}


{Begin Comment Interlisp-10 specific}
{Def {Type (Record Type)} {Name ARRAYBLOCK}
{Text
(Not implemented in Interlisp-D)  Similar to a {lisp DATATYPE} declaration, except that the objects it creates and manipulates are arrays.  As with {lisp DATATYPE}'s, the actual order of the fields of the {lisp ARRAYBLOCK} may be shuffled around in order to satisfy garbage collector constraints.
}}
{End Comment Interlisp-10 specific}



{Def {Type (Record Type)} {Name ACCESSFNS}
{Text
The {lisp ACCESSFNS} record type is used to define data structures with user-defined access functions.  For each field name, the user specifies how it is to be accessed and set.  This allows the use of the record package with arbitrary data structures, with complex access methods.

{arg RECORD-FIELDS} is interpreted as a list of elements of the form {lisp ({arg FIELD-NAME} {arg ACCESSDEF} {arg SETDEF})}.  {arg ACCESSDEF} should be a function of one argument, the datum, and will be used for accessing the value of the field.  {arg SETDEF} should be a function of two arguments, the datum and the new value, and will be used for storing a new value in a field.  {arg SETDEF} may be omitted, in which case, no storing operations are allowed.

{arg ACCESSDEF} and/or {arg SETDEF} may also be a form written in terms of variables {lisp DATUM} and (in {arg SETDEF}) {lisp NEWVALUE}.{index DATUM Var}{index NEWVALUE Var}  For example, given the declaration

{lispcode
[ACCESSFNS FOO
   ((FIRSTCHAR (NTHCHAR DATUM 1)
               (RPLSTRING DATUM 1 NEWVALUE))
   (RESTCHARS (SUBSTRING DATUM 2]}

{lisp (replace (FOO FIRSTCHAR) of X with Y)} would translate to {lisp (RPLSTRING X 1 Y)}.  Since no {arg SETDEF} is given for the {lisp RESTCHARS} field, attempting to perform {lisp (replace (FOO RESTCHARS) of X with Y)} would generate an error, {lisp REPLACE UNDEFINED FOR FIELD}.{index REPLACE UNDEFINED FOR FIELD Error}  Note that {lisp ACCESSFNS} do not have a {lisp CREATE} definition.  However, the user may supply one in the defaults or subdeclarations of the declaration, as described below.  Attempting to {lisp CREATE} an {lisp ACCESSFNS} record without specifying a create definition will cause an error {lisp CREATE NOT DEFINED FOR THIS RECORD}.{index CREATE NOT DEFINED FOR THIS RECORD Error}

{arg ACCESSDEF} and {arg SETDEF} can also be a property list which specify {lisp FAST}, {lisp STANDARD} and {lisp UNDOABLE} versions of the {lisp ACCESSFNS} forms, e.g.

{lispcode
[ACCESSFNS LITATOM
   ((DEF (STANDARD GETD FAST FGETD)
         (STANDARD PUTD UNDOABLE /PUTD]}

means if {lisp FAST} declaration is in effect, use {lisp FGETD} for fetching, if {lisp UNDOABLE}, use {lisp /PUTD} for saving (see CLISP declarations, {PageRef Tag CLISPDeclarations}).


Note:  {arg SETDEF} forms should be written so that they return the new value, to be consistant with {lisp REPLACE} operations for other record types.  The {lisp REPLACE} record operator does not enforce this, though.

{note ACCESSFNS can also be ACCESSFN (ACCESSFN defined as USERRECORDTYPE)}
}}



The {lisp ACCESSFNS} facility allows the use of data structures not specified by one of the built-in record types.  For example, one possible representation of a data structure is to store the fields in {it parallel} arrays, especially if the number of instances required is known, and they do not need to be garbage collected.  Thus, to implement a data structure called {lisp LINK} with two fields {lisp FROM} and {lisp TO}, one would have two arrays {lisp FROMARRAY} and {lisp TOARRAY}.  The representation of an "instance" of the record would be an integer which is used to index into the arrays. This can be accomplished with the declaration:

{lispcode
[ACCESSFNS LINK
   ((FROM (ELT FROMARRAY DATUM)
            (SETA FROMARRAY DATUM NEWVALUE))
    (TO (ELT TOARRAY DATUM)
          (SETA TOARRAY DATUM NEWVALUE)))
    (CREATE (PROG1 (SETQ LINKCNT (ADD1 LINKCNT))
                        (SETA FROMARRAY LINKCNT FROM)
                        (SETA TOARRAY LINKCNT TO)))
    (INIT (PROGN
               (SETQ FROMARRAY (ARRAY 100))
               (SETQ TOARRAY (ARRAY 100))
               (SETQ LINKCNT 0)]}

To create a new {lisp LINK}, a counter is incremented and the new elements stored.  (Note: The {lisp CREATE} form given the declaration probably should include a test for overflow.)


}{End SubSec Record Types}



{Begin SubSec Optional Record Specifications}
{Title Optional Record Specifications}
{Text

After the {arg RECORD-FIELDS} item in a record declaration expression there can be an arbitrary number of additional expressions in {arg RECORD-TAIL}.  These expressions can be used to specify default values for record fields, special {lisp CREATE} and {lisp TYPE?} forms, and subdeclarations.  The following expressions are permitted: 


{Begin LabeledList RECORD-TAIL may contain expressions of the form}

{Label {index *PRIMARY* ← (in record declarations)}{lisp {arg FIELD-NAME} ← {arg FORM}}}
{Item
Allows the user to specify within the record declaration the default value to be stored in {arg FIELD-NAME} by a {lisp CREATE}{index CREATE (Record Operator)} (if no value is given within the {lisp CREATE} expression itself).  Note that {arg FORM} is evaluated at {lisp CREATE} time, not when the declaration is made.
}

{Label {index *PRIMARY* CREATE (in record declarations)}{lisp (CREATE {arg FORM})}}
{Item
Defines the manner in which {lisp CREATE}{index CREATE (Record Operator)} of this record should be performed.  This provides a way of specifying how {lisp ACCESSFNS}{index ACCESSFNS (Record Type)} should be created or overriding the usual definition of {lisp CREATE}.  If {arg FORM} contains the field-names of the declaration as variables, the forms given in the {lisp CREATE} operation will be substituted in.  If the word {lisp DATUM}{index DATUM Var} appears in the create form, the {it original} {lisp CREATE} definition is inserted.  This effectively allows the user to "advise" the create.

Note:  {lisp (CREATE {arg FORM})} may also be specified as "{lisp {arg RECORD-NAME} ← {arg FORM}}".{index ← (in record declarations)}
}

{Label {index *PRIMARY* INIT (in record declarations)}{lisp (INIT {arg FORM})}}
{Item
Specifies that {arg FORM} should be evaluated when the record is declared.  {arg FORM} will also be dumped by the {filecom INITRECORDS} file package command ({PageRef FileCom INITRECORDS}).

For example, see the example of an {lisp ACCESSFNS} record declaration above.  In this example, {lisp FROMARRAY} and {lisp TOARRAY} are initialized with an {lisp INIT} form.
} 


{Label {index *PRIMARY* TYPE? (in record declarations)}{Tag RecordDeclarationTYPE?}{lisp (TYPE? {arg FORM})}}
{Item
Defines the manner in which {lisp TYPE?} expressions are to be translated.  {arg FORM} may either be an expression in terms of {lisp DATUM}{index DATUM var} or a function of one argument.
}

{Label {index *PRIMARY* SUBRECORD (in record declarations)}{lisp (SUBRECORD {arg NAME} . {arg DEFAULTS})}}
{Item
{arg NAME} must be a field that appears in the current declaration and the name of another record.  This says that, for the purposes of translating {lisp CREATE} expressions, substitute the top-level declaration of {arg NAME} for the {lisp SUBRECORD} form, adding on any defaults specified.

For example:  Given {lisp (RECORD B (E F G))}, {lisp (RECORD A (B C D) (SUBRECORD B))} would be treated like {lisp (RECORD A (B C D) (RECORD B (E F G)))} for the purposes of translating {lisp CREATE} expressions.
}


{Label {index *PRIMARY* Subdeclarations}{Tag RecordSubDeclarations}a subdeclaration}
{Item
If a record declaration expression occurs among the record specifications of another record declaration, it is known as a "subdeclaration."  Subdeclarations are used to declare that fields of a record are to be interpreted as another type of record, or that the record data object is to be interpreted in more than one way.

The {arg RECORD-NAME} of a subdeclaration must be either the {arg RECORD-NAME} of its immediately superior declaration or one of the superior's field-names.  Instead of identifying the declaration as with top level declarations, the record-name of a subdeclaration identifies the parent field or record that is being described by the subdeclaration.  Subdeclarations can be nested to an arbitrary depth.

Giving a subdeclaration {lisp (RECORD {arg NAME{sub 1}} {arg NAME{sub 2}})} is a simple way of defining a {it synonym} for the field {arg NAME{sub 1}}.

It is possible for a given field to have more than one subdeclaration. For example, in

{lispcode
(RECORD FOO (A B) (RECORD A (C D)) (RECORD A (Q R)))}

{lisp (Q R)} and {lisp (C D)} are "overlayed," i.e. {lisp (fetch Q of X)} and {lisp (fetch C of X)} would be equivalent.  In such cases, the {it first} subdeclaration is the one used by {lisp CREATE}.
}

{Label {index *PRIMARY* SYNONYM (in record declarations)}{lisp (SYNONYM {arg FIELD} ({arg SYN{sub 1}} {ellipsis} {arg SYN{sub N}}))}}
{Item
{arg FIELD} must be a field that appears in the current declaration.  This defines {arg SYN{sub 1}} {ellipsis} {arg SYN{sub N}} all as synonyms of {arg FIELD}.  If there is only one synonym, this can be written as {lisp (SYNONYM {arg FIELD} {arg SYN})}.
}

{Label {index *PRIMARY* SYSTEM (in record declarations)}{lisp (SYSTEM)}}
{Item
If {lisp (SYSTEM)} is included in a record declaration, this indicates that the record is a "system" record rather than a "user" record.  The only distinction between the two types of records is that "user" record declarations take precedence over "system" record declarations, in cases where an unqualified field name would be considered ambiguous.  All of the records defined in the standard Interlisp-D system are defined as system records.
}


{End LabeledList RECORD-TAIL may contain expressions of the form}


}{End SubSec Optional Record Specifications}


}{End SubSec Record Declarations}




{Begin SubSec Defining New Record Types}
{Title Defining New Record Types}
{Text

In addition to the built-in record types, users can declare their own record types by performing the following steps:


(1)  Add the new record-type to the value of {var CLISPRECORDTYPES};{index CLISPRECORDTYPES Var}.


(2)  Perform {lisp (MOVD 'RECORD {arg RECORD-TYPE})}, i.e. give the record-type the same definition as that of the function {lisp RECORD};


(3)  Put the name of a function which will return the translation on the property list of {arg RECORD-TYPE}, as the value of the property {prop USERRECORDTYPE}{index USERRECORDTYPE Prop}.  Whenever a record declaration of type {arg RECORD-TYPE} is encountered, this function will be passed the record declaration as its argument, and should return a {it new} record declaration which the record package will then use in its place.


}{End SubSec Defining New Record Types}





{Begin SubSec Record Manipulation Functions}
{Title Record Manipulation Functions}
{Text


{FnDef {FnName EDITREC} {FnArgs NAME COM{sub 1} {ellipsis} COM{sub N}}
{Type NLAMBDA NOSPREAD}
{Text
{fn EDITREC} calls the editor on a copy of all declarations in which {arg NAME} is the record name or a field name.  On exit, it redeclares those that have changed and undeclares any that have been deleted.  If {arg NAME} is {lisp NIL}, {it all} declarations are edited.

{arg COM{sub 1}} {ellipsis} {arg COM{sub N}} are (optional) edit commands.
}}



When the user redeclares a global record, the translations of all expressions involving that record or any of its fields are automatically deleted from {var CLISPARRAY}, and thus will be recomputed using the new information.  If the user changes a {it local} record declaration ({PageRef Term Local Record Declarations}), or changes some other CLISP declaration ({PageRef Term CLISP declarations}), e.g., {lisp STANDARD} to {lisp FAST}, and wishes the new information to affect record expressions already translated, he must make sure the corresponding translations are removed, usually either by {fn CLISPIFY}ing or using the {lisp DW} edit macro.



{FnDef {FnName RECLOOK} {FnArgs RECNAME {anonarg} {anonarg} {anonarg} {anonarg}}
{Text
Returns the entire declaration for the record named {arg RECNAME}; {lisp NIL} if there is no record declaration with name {arg RECNAME}.  Note that the record package maintains internal state about current record declarations, so performing destructive operations (e.g. {fn NCONC}) on the value of {fn RECLOOK} may leave the record package in an inconsistent state.  To change a record declaration, use {fn EDITREC}.
}}



{FnDef {FnName FIELDLOOK} {FnArgs FIELDNAME}
{Text
Returns the list of declarations in which {arg FIELDNAME} is the name of a field.
}}



{FnDef {FnName RECORDFIELDNAMES} {FnArgs RECORDNAME {anonarg}}
{Text
Returns the list of fields declared in record {arg RECORDNAME}.  {arg RECORDNAME} may either be a name or an entire declaration.
}}



{FnDef {FnName RECORDACCESS} {FnArgs FIELD DATUM DEC TYPE NEWVALUE}
{Text
{arg TYPE} is one of {lisp FETCH}, {lisp REPLACE}, {lisp FFETCH}, {lisp FREPLACE}, {lisp /REPLACE} or their lowercase equivalents.  {arg TYPE}={lisp NIL} means {lisp FETCH}.  If {arg TYPE} corresponds to a fetch operation, i.e. is {lisp FETCH}, or {lisp FFETCH}, {fn RECORDACCESS} performs {lisp ({arg TYPE} {arg FIELD} of {arg DATUM})}.  If {arg TYPE} corresponds to a replace, {fn RECORDACCESS} performs {lisp ({arg TYPE} {arg FIELD} of {arg DATUM} with {arg NEWVALUE})}.  {arg DEC} is an optional declaration; if given, {arg FIELD} is interpreted as a field name of that declaration.

Note that {fn RECORDACCESS} is relatively inefficient, although it is better than constructing the equivalent form and performing an {fn EVAL}.
}}


{FnDef {FnName RECORDACCESSFORM} {FnArgs FIELD DATUM TYPE NEWVALUE}
{Text
Returns the form that would be compiled as a result of a record access.  {arg TYPE} is one of {lisp FETCH}, {lisp REPLACE}, {lisp FFETCH}, {lisp FREPLACE}, {lisp /REPLACE} or their lowercase equivalents.  {arg TYPE}={lisp NIL} means {lisp FETCH}.  
}}



{Begin Note}
Date:  5 NOV 1974 2339-PST
From: MASINTER
Subject: RECORD USE
To:   KAPLAN

If RECORDUSE is both set and defined, then whenever the record package makes a translation, it will call (RECORDUSE functionname recordname fieldnames).  Functionname is the name of the function containing the reference; either recordname or fieldnames are non-nil. On CREATE's and TYPE?'s, its recordname; on FETCH's and REPLACE's it's fieldnames (which is a list of field names).

In addition, whenever a record is redeclared, the record package function RECREDECLARE[redeclarelst,recname] is called (redeclarelst is the list of field names). You can advise this function to, say, unsavedef all the functions which use either that record name or any of those fields.
{End Note}



}{End SubSec Record Manipulation Functions}




{Begin SubSec Changetran}
{Title Changetran}
{Text

{note Changetran was designed by R. M. Kaplan.}

{index *BEGIN* Changetran}

A very common programming construction consists of assigning a new value to some datum that is a function of the current value of that datum.  Some examples of such read-modify-write sequences include:

Incrementing a counter:

{lispcode (SETQ X (IPLUS X 1))}

Pushing an item on the front of a list:

{lispcode (SETQ X (CONS Y X))}

Popping an item off a list:

{lispcode (PROG1 (CAR X) (SETQ X (CDR X)))}

It is easier to express such computations when the datum in question is a simple variable as above than when it is an element of some larger data structure.  For example, if the datum to be modified was {lisp (CAR X)}, the above examples would be:

{lispcode (CAR (RPLACA X (IPLUS (CAR X) 1)))}

{lispcode (CAR (RPLACA X (CONS Y (CAR X)))}

{lispcode (PROG1 (CAAR X) (RPLACA X (CDAR X)))}

and if the datum was an element in an array, {lisp (ELT A N)}, the examples would be:

{lispcode (SETA A N (IPLUS (ELT A N) 1)))}

{lispcode (SETA A N (CONS Y (ELT A N))))}

{lispcode (PROG1 (CAR (ELT A N)) (SETA A N (CDR (ELT A N))))}


The difficulty in expressing (and reading) modification idioms is in part due to the well-known asymmetry of setting versus accessing operations on structures:  {fn RPLACA} is used to set what {fn CAR} would return, {fn SETA} corresponds to {fn ELT}, and so on.

The "Changetran" facility is designed to provide a more satisfactory notation in which to express certain common (but user-extensible) structure modification operations.  Changetran defines a set of CLISP words that encode the kind of modification that is to take place, e.g. pushing on a list, adding to a number, etc.  More important, the expression that indicates the datum whose value is to be modified needs to be stated only once.  Thus, the "change word" {lisp ADD} is used to increase the value of a datum by the sum of a set of numbers.  Its arguments are an expression denoting the datum, and a set of items to be added to its current value.  The datum expression must be a variable or an accessing expression (envolving {lisp FETCH}, {lisp CAR}, {lisp LAST}, {fn ELT}, etc) that can be translated to the appropriate setting expression.


{note above needs better defn.  at least, give more complete list of accessing functions that can be in DATUM form.}


For example, {lisp (ADD (CADDR X) (FOO))} is equivalent to:

{lispcode
(CAR (RPLACA (CDDR X)
             (PLUS (FOO) (CADDR X)))}

If the datum expression is a complicated form involving subsidiary function calls, such as {lisp (ELT (FOO X) (FIE Y)))}, Changetran goes to some lengths to make sure that those subsidiary functions are evaluated only once (it binds local variables to save the results), even though they logically appear in both the setting and accessing parts of the translation.  Thus, in thinking about both efficiency and possible side effects, the user can rely on the fact that the forms will be evaluated only as often as they appear in the expression. 


For {lisp ADD} and all other changewords, the lower-case version ({lisp add}, etc.) may also be specified.  Like other CLISP words, change words are translated using all CLISP declarations in effect (see {PageRef Term CLISP declarations}).


The following is a list of those change words recognized by Changetran.  Except for {lisp POP}, the value of all built-in changeword forms is defined to be the new value of the datum.


{Def {Type (Change Word)}
{Name ADD} {Args DATUM ITEM{sub 1} ITEM{sub 2} {ellipsis}}
{Text
Adds the specified items to the current value of the datum, stores the result back in the datum location.  The translation will use {fn IPLUS}, {fn PLUS}, or {fn FPLUS} according to the CLISP declarations in effect (see {PageRef Term CLISP declarations}).
}}


{Def {Type (Change Word)}
{Name PUSH} {Args DATUM ITEM{sub 1} ITEM{sub 2} {ellipsis}}
{Text
{fn CONS}es the items onto the front of the current value of the datum, and stores the result back in the datum location.  For example, {lisp (PUSH X A B)} would translate as {lisp (SETQ X (CONS A (CONS B X)))}.
}}


{Def {Type (Change Word)}
{Name PUSHNEW} {Args DATUM ITEM}
{Text
Like {lisp PUSH} (with only one item) except that the item is not added if it is already {fn FMEMB} of the datum's value.

Note that, whereas {lisp (CAR (PUSH X 'FOO))} will always be {lisp FOO},
{lisp (CAR (PUSHNEW X 'FOO))} might be something else if {lisp FOO} already existed in the middle of the list.
}}


{Def {Type (Change Word)}
{Name PUSHLIST} {Args DATUM ITEM{sub 1} ITEM{sub 2} {ellipsis}}
{Text
Similar to {lisp PUSH}, except that the items are {fn APPEND}ed in front of the current value of the datum.  For example, {lisp (PUSHLIST X A B)} would translate as {lisp (SETQ X (APPEND A B X))}.
}}


{Def {Type (Change Word)}
{Name POP} {Args DATUM}
{Text
Returns {fn CAR} of the current value of the datum after storing its {fn CDR} into the datum.  The current value is computed only once even though it is referenced twice.  Note that this is the only built-in changeword for which the value of the form is not the new value of the datum.
}}


{Def {Type (Change Word)}
{Name SWAP} {Args DATUM{sub 1} DATUM{sub 2}}
{Text
Sets {arg DATUM{sub 1}} to {arg DATUM{sub 2}} and vice versa.

{note returns??}
}}


{Def {Type (Change Word)}
{Name CHANGE} {Args DATUM FORM}
{Text
This is the most flexible of all change words, since it enables the user to provide an arbitrary form describing what the new value should be, but it still highlights the fact that structure modification is to occur, and still enables the datum expression to appear only once.  {lisp CHANGE} sets {arg DATUM} to the value of {arg FORM*}, where {arg FORM*} is constructed from {arg FORM} by substituting the datum expression for every occurrence of the litatom {lisp DATUM}.{index DATUM (in Changetran)}  For example, {lisp (CHANGE (CAR X) (ITIMES DATUM 5))} translates as {lisp (CAR (RPLACA X (ITIMES (CAR X) 5)))}.

{lisp CHANGE} is useful for expressing modifications that are not built-in and are not sufficiently common to justify defining a user-changeword.  As for other changeword expressions, the user need not repeat the datum-expression and need not worry about multiple evaluation of the accessing form. 
}}




It is possible for the user to define new change words.  To define a change word, say {lisp sub}, that subtracts items from the current value of the datum, the user must put the property {prop CLISPWORD},{index CLISPWORD Prop} value {lisp (CHANGETRAN . sub)} on both the upper and lower-case versions of {lisp sub}:

{lispcode
(PUTPROP 'SUB 'CLISPWORD '(CHANGETRAN . sub))
(PUTPROP 'sub 'CLISPWORD '(CHANGETRAN . sub))}

Then, the user must put (on the {it lower}-case version of {lisp sub} only) the property {prop CHANGEWORD},{index CHANGEWORD Prop} with value {arg FN}.  {arg FN} is a function that will be applied to a single argument, the whole {lisp sub} form, and must return a form that Changetran can translate into an appropriate expression.  This form should be a list structure with the atom {lisp DATUM} used whenever the user wants an accessing expression for the current value of the datum to appear.  The form {lisp (DATUM← {arg FORM})} (note that {lisp DATUM←} is a single atom) should occur once in the expression; this specifies that an appropriate storing expression into the datum should occur at that point.  For example, {lisp sub} could be defined with:

{lispcode
(PUTPROP 'sub 'CHANGEWORD
         '(LAMBDA (FORM)
            (LIST 'DATUM←
                  (LIST 'IDIFFERENCE
                        'DATUM
                        (CONS 'IPLUS (CDDR FORM))))))}

If the expression {lisp (sub (CAR X) A B)} were encountered, the arguments to {lisp SUB} would first be dwimified, and then the {prop CHANGEWORD} function would be passed the list {lisp (sub (CAR X) A B)}, and return {lisp (DATUM← (IDIFFERENCE DATUM (IPLUS A B)))}, which Changetran would convert to {lisp (CAR (RPLACA X (IDIFFERENCE (CAR X) (IPLUS A B))))}.

Note:  The {lisp sub} changeword as defined above will always use {fn IDIFFERENCE} and {fn IPLUS}; {lisp add} uses the correct addition operation depending on the current CLISP declarations (see {PageRef Term CLISP declarations}).



{index *END* Changetran}

}{End SubSec Changetran}



{Begin SubSec Built-In and User Data Types}
{Title Built-In and User Data Types}
{Text

{index *PRIMARY* Data types}
{index *PRIMARY* User data types}

Interlisp is a system for the manipulation of various kinds of data; it provides a large set of built-in data types, which may be used to represent a variety of abstract objects, and the user can also define additional "user data types" which can be manipulated exactly like built-in data types.

{index *PRIMARY* Type names of data types}
{index *PRIMARY* Data type names}

Each data type in Interlisp has an associated "type name," a litatom.  Some of the type names of built-in data types are:  {lisp LITATOM}, {lisp LISTP}, {lisp STRINGP}, {lisp ARRAYP}, {lisp STACKP}, {lisp SMALLP}, {lisp FIXP}, and {lisp FLOATP}.  For user data types, the type name is specified when the data type is created.


{FnDef {FnName DATATYPES} {FnArgs {anonarg}}
{Text
Returns a list of all type names currently defined.

{note if arg USERSFLG=non-NIL, doesn't include built-in data types. doc?}
}}

{FnDef {FnName USERDATATYPES} {FnArgs}
{Text
Returns list of names of currently declared user data types.

{Begin Note}
Date: 14 Aug 85 13:58 PDT
From: Sannella.pa
(4)  What is the difference between user data types and system data types?  Currently (in Full.sysout), there are 72 user data types and 9 non-user data types.
Date: 14 Aug 85 20:33 PDT
From: Masinter.pa
The difference between "user" datatypes and "system" datatypes has been blurred. Perhaps we should remove the notion of "user" datatypes and just say that (DATATYPES) returns the list of *all* datatypes known to the system.
{End Note}
}}

{FnDef {FnName TYPENAME} {FnArgs DATUM}
{Text
Returns the type name for the data type of {arg DATUM}.
}}


{FnDef {FnName TYPENAMEP} {FnArgs DATUM TYPE}
{Text 
Returns {lisp T} if {arg DATUM} is an object with type name equal to {arg TYPE}, otherwise {lisp NIL}.
}}


Note:  {fn TYPENAME} and {fn TYPENAMEP} distinguish the logical data types {lisp ARRAYP}, {lisp CCODEP} and {lisp HARRAYP}, even though they may be implemented as {lisp ARRAYP}s in some Interlisp implementations.


In addition to built-in data-types such as atoms, lists, arrays, etc., Interlisp provides a way of defining completely {it new} classes of objects, with a fixed number of fields determined by the definition of the data type.  In order to define a new class of objects, the user must supply a name for the new data type and specifications for each of its fields.  Each field may contain either a pointer (i.e., any arbitrary Interlisp datum), an integer, a floating point number, or an {arg N}-bit integer.

Note:  The most convenient way to define new user data types is via {lisp DATATYPE} record declarations ({PageRef (Record Type) DATATYPE}) which call the following functions.


{FnDef {FnName DECLAREDATATYPE} {FnArgs TYPENAME FIELDSPECS {anonarg} {anonarg}}
{Text
Defines a new user data type, with the name {arg TYPENAME}.  {arg FIELDSPECS} is a list of "field specifications."  Each field specification may be one of the following:

{Begin labeledlist DECLAREDATATYPE field specification}

{Name {lisp POINTER}{index POINTER (as a field specification)}}
{Text
Field may contain any Interlisp datum.
}

{Name {lisp FIXP}{index FIXP (as a field specification)}}
{Text 
Field contains an integer.
}

{Name {lisp FLOATP}{index FLOATP (as a field specification)}}
{Text 
Field contains a floating point number.
}

{Name {lisp (BITS {arg N})}{index BITS (as a field specification)}}
{Text 
Field contains a non-negative integer less than 2{super {arg N}}.
}

{Name {lisp BYTE}{index BYTE (as a field specification)}}
{Text 
Equivalent to {lisp (BITS 8)}.
}

{Name {lisp WORD}{index WORD (as a field specification)}}
{Text 
Equivalent to {lisp (BITS 16)}.
}

{Name {lisp SIGNEDWORD}{index SIGNEDWORD (as a field specification)}}
{Text 
Field contains a 16 bit signed integer.
}

{End labeledlist DECLAREDATATYPE field specification}


{fn DECLAREDATATYPE} returns a list of "field descriptors," one for each element of {arg FIELDSPECS}.  A field descriptor contains information about where within the datum the field is actually stored.

If {arg FIELDSPECS} is {lisp NIL}, {arg TYPENAME} is "undeclared."  If {arg TYPENAME} is already declared as a data type, it is undeclared, and then re-declared with the new {arg FIELDSPECS}.  An instance of a data type that has been undeclared has a type name of {indexX {Name DEALLOC} {Type data type name} {Text {lisp **DEALLOC**}} }{index *PRIMARY* **DEALLOC** (data type name)}{lisp **DEALLOC**}.
}}


{FnDef {FnName FETCHFIELD} {FnArgs DESCRIPTOR DATUM}
{Text
Returns the contents of the field described by {arg DESCRIPTOR} from {arg DATUM}.  {arg DESCRIPTOR} must be a "field descriptor" as returned by {fn DECLAREDATATYPE} or {fn GETDESCRIPTORS}.  If {arg DATUM} is not an instance of the datatype of which {arg DESCRIPTOR} is a descriptor, causes error {lisp DATUM OF INCORRECT TYPE}{index DATUM OF INCORRECT TYPE Error}.
}}


{FnDef {FnName REPLACEFIELD} {FnArgs DESCRIPTOR DATUM NEWVALUE}
{Text
Store {arg NEWVALUE} into the field of {arg DATUM} described by {arg DESCRIPTOR}.  {arg DESCRIPTOR} must be a field descriptor as returned by {fn DECLAREDATATYPE}.  If {arg DATUM} is not an instance of the datatype of which {arg DESCRIPTOR} is a descriptor, causes error {lisp DATUM OF INCORRECT TYPE}.  Value is {arg NEWVALUE}.
}}


{FnDef {FnName NCREATE} {FnArgs TYPE OLDOBJ}
{Text
Creates and returns a new instance of datatype {arg TYPE}.

If {arg OLDOBJ} is also a datum of datatype {arg TYPE}, the fields of the new object are initialized to the values of the corresponding fields in {arg OLDOBJ}.

{fn NCREATE} will not work for built-in datatypes, such as {lisp ARRAYP}, {lisp STRINGP}, etc.  If {arg TYPE} is not the type name of a previously declared {it user} data type, generates an error, {lisp ILLEGAL DATA TYPE}.{index ILLEGAL DATA TYPE Error}


{note In Interlisp-D, NCREATE will work for Some built-in datatypes - jonl}
}}


{FnDef {FnName GETFIELDSPECS} {FnArgs TYPENAME}
{Text
Returns a list which is {fn EQUAL} to the {arg FIELDSPECS} argument given to {fn DECLAREDATATYPE} for {arg TYPENAME}; if {arg TYPENAME} is not a currently declared data-type, returns {lisp NIL}.
}}


{FnDef {FnName GETDESCRIPTORS} {FnArgs TYPENAME}
{Text
Returns a list of field descriptors, {fn EQUAL} to the {it value} of {fn DECLAREDATATYPE} for {arg TYPENAME}.  If {arg TYPENAME} is not an atom, {lisp (TYPENAME {arg TYPENAME})} is used.
}}



Note that the user can define how user data types are to be printed via {fn DEFPRINT} ({PageRef Fn DEFPRINT}), how they are to be evaluated by the interpreter via {fn DEFEVAL} ({PageRef Fn DEFEVAL}), and how they are to be compiled by the compiler via {var COMPILETYPELST} ({PageRef Var COMPILETYPELST}).



{note In Interlisp-10, user datatypes are allocated starting with type number 31.  The minimum amount of space that can be assigned to any one data-type is one page, thus effectively limiting the number of possible data types.  The fields are rearranged internally so that pointer fields come first; the remaining numeric fields, if any, are packed so as to optimize the space used.}



{note from the mail archives: (Larry, 9/9/82)
Interlisp-D does not currently support datatypes larger than 256 words (128 pointers).  Bigger data structures must be allocated out of array space.  There is a bug in the current release so that NO ERROR MESSAGE is generated when declaring such a beast --- rather the system happily creates one, which eventually clobbers other pages in the working environment.
(will be changed, but limit still exists --BVM)

still true?  documentP?  (currently max size is 512 bytes --lmm)}

{Note
Interlisp-10 : limited # of datatypes to 64
Interlisp-D :  limit currently 256, extension to 2↑15 planned   ---lmm}


{note in Interlisp-Vax, minimum amount of storage per datatype is 64K bytes.  Nax number of types is about 3600}


}{End SubSec Built-In and User Data Types}





}{End Chapter Record Package}