type PARENT(D : BOOLEAN := TRUE) is record A : INTEGER; case D is when TRUE => U : INTEGER; V : INTEGER; when FALSE => W : REAL; end case; end record; |
From a logical point of view, derivation will produce a copy of the parent type:
type COPY is new PARENT;
This means that COPY and PARENT have the same components, including discriminants, and components that are declared in variants. Having two types, we can specify two (different) representations; for example:
for PARENT use record -- a sparse representation that optimizes -- efficiency of access to components end record; for COPY use record -- a compact representation -- to be stored on secondary storage end record; |
For change of representation we can exploit the fact that the two types are derived from each other and use explicit conversion:
declare C : COPY; P : PARENT; begin READ_FROM_DISK(C); P := PARENT(C); -- convert to PARENT form OPERATE_EFFICIENTLY_ON (P); C := COPY(P); -- convert back to COPY form WRITE_TO_DISK(C); end; |
or simply:
declare C : COPY; begin READ_FROM_DISK(C); OPERATE_EFFICIENTLY_ON(PARENT(C)); WRITE_TO_DISK(C); end; |
Aside from derivation, there is actually no satisfactory way to achieve this change of representation. Consider for example the alternative of copying (whether manually or with text editors) the type definition of PARENT when defining the type COPY:
type COPY(D : BOOLEAN := TRUE) is record A : INTEGER; case D is when TRUE => U : INTEGER; V : INTEGER; when FALSE => W : REAL; end case; end record; |
To achieve change of representation, we must first realize that the obvious idea - component-by-component assignment - will not work:
P.D := C.D; -- Illegal! P.A := C.A; case C.D is when TRUE => P.U := C.U; P.V := C.V; when FALSE => P.W := C.W; end case; |
This is the equivalent of the code that will be generated by a compiler for the conversion for change of representation. But it cannot be written directly by the programmer since direct assignment to a discriminant is not allowed: discriminant values may only be changed by whole record assignments. Therefore, the solution to the above problem is to write:
case C.D is when TRUE => P := (TRUE, C.A, C.V, C.U); when FALSE => P := (FALSE, C.A, C.W); end case; |
This solution is wordy and again requires a manual copy of the record structure; it is therefore likely to create errors (such as interchanging U and V above - did you spot it?). Furthermore, it suffers from the maintenance problems that are inherent in any solution that requires text duplication. Thus if the type definition is ever extended, corresponding changes need to be performed in the above case statement. Their complexity - and hence the likelihood of error - will increase with the size of the record type definition.
Note finally that defining the type PARENT in a generic package and creating copies by generic instantiation is not a solution to our problem: All instantiations would result in types that have the same representation, and conversions between such types would not be available.