Heading:
IDL DESIGN: Page heading
Page Numbers: Yes X: 527 Y: 10.5"
XEROX Palo Alto Research Center 25 March 1977
Inter-Office Memorandum
ToIDL GroupIDL Design Note Number: 9
FromRon Kaplan, Beau SheilFile:<IDL>Design.RowTypes
Subject Rows, element types, and conversions
IDL ROWSCALARs have two dimensions of freedom. Their element mode may be either INT or FLOAT and they may or may not have NIL elements. These properties are to be preserved by constraining the SETRELT function, in order to guarrantee the efficiency of arithmetic on the results of GETRELT operations. Thus, only if a row has NILs (or the "possibility" of NILs) may (further) NILs be stored into it. If its elements are FLOAT, then either INTs or FLOATs may be stored into it. In general, unless the type of the row "covers" the type being stored, the store operation is in error (i.e. in particular, SETRELT does not FIX FLOATs being stored into ROWINTs).
If (and only if) both the type and freedom from NILs are known at compile time, significant optimization of GETRELTs (and thus, of the subsequent arithmetic) can be done. Our choice of data types reflects this fact. It leads us to three varieties of arithmetic rows
ROWSCALARs: In principle, all arithmetic rows are ROWSCALARs, although some may be more constrained. Unless otherwise constrained (see below for methods of specifying constraint), a ROWSCALAR may contain NILs and will be of element type FLOAT (i.e. it is completely unconstrained with respect to SETRELT, and completely unoptimized with respect to GETRELT).
ROWINTs, ROWFLOATs: These are NIL free and of homogenous element data type. They are included in the system purely for the efficiency that is obtainable in arithmetic if both these attributes are known at compile time.
The general principle from which all this comes is
NO SYSTEM CODE WILL IMPLICITLY CAUSE INFORMATION TO BE LOST.
Examples of such operations include FIXing a floating point number inside the SETRELT routine when storing into an INTEGER row, or changing the NILfreeness of a row by storing a NIL into it. As an immediate corrollary, we have
NO ATTRIBUTE OF ANY ROW WILL CHANGE "UNDERNEATH" A SYSTEM FUNCTION.
The importance of this is that system code may assume that a declared ROWINT or ROWFLOAT will remain a ROWINT or ROWFLOAT and that efficient access functions may therefore be compiled for it. Conversely,
THE ONLY ATTRIBUTE OF A ROW WHICH AFFECTS THE EFFICIENCY OF ITS ACCESSES ARE THOSE THAT ARE SPECIFIED AT COMPILE TIME.
In particular, if a row is declared to be a ROWSCALAR (even if the element mode and NIL flag are specified to be the same as for ROWINTs), slow access functions will be used.
These principles lead to the following implementation. ROWs carry two fields: The RELTTYPE (INTEGER, FLOAT (or PTR - not discussed here)) and a CANSETNIL bit. ROWINTs and ROWFLOATs both have this bit set off. SETRELT enforces the convention that NILs are not set into NIL free arrays, and that FLOATs are not stored into INTEGER arrays, by refusing to store inconsistent values into an array (Stores and fetches from ROWINTs and ROWFLOATs will be assumed correct in a production version, and no checking will be done then). An object which is declared ROWSCALAR must be assumed to have any of the four possible combinations of mode/NIL fields, and may be created with any possible combination (using the CREATE fields RELTTYPE and CANSETNIL) which will cause checking by SETRELT but may not lead to any noticeable efficiencies.
CREATE sets the type of a ROW as follows: The RELTYPE must be given in the CREATE and the INIT value must be compatible with that type. CANSETNIL is T unless specified otherwise in the CREATE.
CONV.ROWSCALAR is now defined to return a ROW with the minimal amount of "freedom" found in its input. If your code assumes the setting of one or more of the mode/NIL fields, you should either use CONV.ROWINT/ROWFLOAT if applicable, check the setting, or copy the object into a row with the correct setting that you make with CREATE. The contention is that as there is no efficiency to be gained by saying, for example, that the row is either INTEGER or NIL, there is no need to CONV to that. A predicate to test the NILfreeness of a row may be produced if widely used (e.g. to reject unacceptable inputs). Note, however, that NOT(CANSETNIL) implies there are no NILs in the row, even though the converse does not follow (i.e. a row may not have had any NILs put in it, even though that would be legal - in fact this will be the normal case).
CONV.ROWINT or CONV.ROWFLOAT are now defined to try to return their input argument if the field settings are OK. Else, if the RELTTYPE is OK but CANSETNIL is T, then, if there are in fact no NILs in the row, and the reference count is 0 (i.e. noone else has this object), the NIL bit is zapped, and the object returned. Else the row is copied.
In general, the only functions that are to change CANSETNIL are the conversion functions and IDL.ASSIGN (and then only with care and on private - not shared - rows). The theory is that these settings are only changed by a very few people (primarily on user objects) and that at that time these objects are always private copies. Therefore, the system code can forget about changes of mode and compile efficiently without a care.