AdaPower Logged in as Guest
Ada Tools and Resources

Ada 95 Reference Manual
Ada Source Code Treasury
Bindings and Packages
Ada FAQ


Join >
Articles >
Ada FAQ >
Getting Started >
Home >
Books & Tutorials >
Source Treasury >
Packages for Reuse >
Latest Additions >
Ada Projects >
Press Releases >
Ada Audio / Video >
Home Pages >
Links >
Contact >
About >
Login >
Back
Why are Controlled types so, well, strange?

We considered many approaches to user-defined finalization and user-defined assignment. Ada presents challenges that make it harder to define assignment than in other languages, because assignment is used implicitly in several operations (by-copy parameter passing, function return, aggregates, object initialization, initialized allocators, etc.), and because Ada has types whose set of components can be changed as a result of an assignment.

For example:

     type T (D : Boolean := False) is record
       case D is
         when False => null;
         when True => H : In_Hands;
       end case;
     end record;

     X,Z : T;
     Y : T := (True, H => ...);

     ...

     X := Y;   -- "X.H" component coming into existence
     Y := Z;   -- "Y.H" component going out of existence
With a type like the one above, there are components that can come and go as a result of assignment. The most obvious definition of assignment would be:
     procedure ":=" (Left : in out In_Hands; Right : in In_Hands);
Unfortunately, this wouldn't work for the "H" component, because there is no preexisting "In_Hands" component to be assigned into in the first case, and in the second case, there is no "In_Hands" component to assign "from."

Therefore, we decided to decompose the operation of assignment into separable pieces: finalization of the left hand side; simple copying of the data from the right hand side to the left hand side; and then adjustment of the new left hand side. Other decompositions are probably possible, but they generally suffer from not being easily composable, or not handling situations like the variant record above.

Imagine a function named ":=" that returns a copy of its in parameter. To do anything interesting it will have to copy the in parameter into a local variable, and then "fiddle" with that local variable (essentially what "Adjust" does), and then return that local variable (which will make yet another copy). The returned result will have to be put back into the desired place (which might make yet another copy). For a large object, this might involve several extra copies.

By having the user write just that part of the operation that "fiddles" with the result after making a copy, we allow the implementation to eliminate redundant copying. Furthermore, some user-defined representations might be position dependent. That is, the final "fiddling" has to take place on the object in its final location. For example, one might want the object to point to itself. If the implementation copies an object after the user code has adjusted it, such self-references will no longer point to the right place.

So, as usual, once one gets into working out the details and all the interactions, the "obvious" proposal (such as a procedure ":=") no longer looks like the best answer, and the best answer one can find potentially looks "clumsy" (at least before you try to work out the details of the alternatives).

(Tucker Taft)


(c) 1998-2004 All Rights Reserved David Botton