[comp.lang.ada] Restricting := versus restricting =

NCOHEN@IBM.COM (Norman COHEN) (01/11/88)

I would guess that most people, upon learning about limited private
types, question the apparently arbitrary linkage between assignment and
equality.  What surprises me is just how often in practice this linkage
turns out to be appropriate, or at least innocuous.  Until Robert
Firth's recent message, I had assumed that some Fundamental Truth about
the relationship between assignment and equality had been revealed to
the Founding Fathers.  What I now see is simply a fortunate coincidence
that causes the linkage between restricting assignment and restricting
equality to be appropriate more often than it is inappropriate.

In "Ada as a Second Language," I identified four general reasons for
making a type limited:

1. Because an operation on an object of the type can affect the state of
   some entity indirectly referenced by that object.  Examples include
   insertion in a linked list.  Language-defined types made limited for
   this reason include file types, whose objects indirectly reference
   external files, and task types, whose objects indirectly reference
   processes (i.e., they "designate tasks").  Predefined assignment
   would cause two objects to indirectly reference the same entity, so
   that an operation on one object of would appear to affect the other
   object as well.

2. Because there are multiple representations of the same abstract
   value.  Examples include stacks, for which array representations
   denote the same value even if the parts of the array above the
   top-of-stack index have different contents; rational numbers, unless
   they are represented exclusively in reduced form; and any type whose
   values are represented as pointers to some structure, if distinct
   instances of the structure with identical contents can arise.

3. Because it is necessary to assert control over the initial value of a
   type, e.g., to initialize a flag that can be used to ensure that no
   object is used before it has been passed to a registration routine.
   A language-defined example is the requirement that internal files be
   passed to OPEN before they are used in data-transmitting operations.

4. Because it is necessary to perform some additional operation each time
   a new copy is made of a value in a type.  Examples include the
   maintenance of reference counts or the deallocation of the old
   contents of the target variable in an assignment.

(I have no reason to think that all reasons for using limited types are
covered by these four cases.  I would be eager to receive net mail from
anyone who can add to this list.)

Reasons 1, 3, and 4 are reasons for a type with restricted assignment.
Reason 2 is a reason for a type with restricted equality.  However, the
various reasons for using limited types tend to overlap in practice.
For example, whenever a type represented as an access type has both
operations (on the access type) that modify the state of the designated
variable and the possibility of two distinct access types designating
variables with the same contents, both Reason 1 and Reason 2 apply.
Furthermore, I have yet to see an application of Reason 4 that does not
involve access types.

Types for which Reason 3 applies tend to be types whose objects have
complex internal states with many components, and thus for which
equality is rarely an interesting operation, so equality is usually not
missed when a type is made limited for Reason 3.

Ada programmers begin to grumble when confronted with types for which
Reason 2 applies, but Reason 1 does not.  Then the need for a
user-defined equality leads, for no good reason, to the need for a
user-defined copying operation.  The unreduced rational-number type is
such a type.

A user-defined ":=" would not be useful when Reason 1 applies, because
the notion of indirect reference in this case is part of the type's
abstract definition, not just part of its implementation.  Therefore,
ANY copying operation is inappropriate.  In contrast, user-defined ":="
would ease the pain when Reason 2 applies but Reason 1 does not.  Reason
3 is a reason for prohibiting the assignment operation in object
initialization, but not for prohibiting the assignment operation in an
assignment statement.  (One also wants to continue prohibiting
value-creating operations like catenation, aggregate formation, and
allocation when Reason 3 applies.) If we agree that the user-defined
procedure is invoked only by an assignment statement and not by an
object initialization, a user-defined ":=" could be useful in this case,
but many programmers would be confused by the prohibition of
"A: Integer := B;" when "A := B;" is allowed.  Finally, Reason 4
specifically assumes that there will be a user-defined copying operation.
It would be preferable to call this operation ":=" rather than COPY.