comp.lang.ada
 help / color / mirror / Atom feed
From: ncohen@watson.ibm.com (Norman H. Cohen)
Subject: Re: Motivating inheritance and dyn. poly.
Date: 21 Nov 1994 15:33:03 GMT
Date: 1994-11-21T15:33:03+00:00	[thread overview]
Message-ID: <3aqejf$o55@watnews1.watson.ibm.com> (raw)
In-Reply-To: 3ai9g8$5e6@israel-info.datasrv.co.il

In article <3ai9g8$5e6@israel-info.datasrv.co.il>,
benari@zeus.datasrv.co.il (Moti Ben-Ari) writes: 

|> I believe that the normative use of variants is to represent
|> true alternates, e.g. a message which can take dozens of forms.
|> On the other hand, many try to use it to save a couple of words
|> of memory as if we were still programming for the dear,
|> departed 16K PDP-11.
|>
|> The example in the Highlight section of the Rat. is typical.
|> There is no reason why a simple record could not be used: 
|>
|>   type Alert is
|>     record
|>       P: Priority;
|>       Time_of...
|>       Message...
|>       Action...
|>       Ring...
|>     end;
|>
|> with null values or pointers used for non-relevant fields.
|> Processing can be done using __non-nested__ if's or case's: 
|>
|>   if (A.Priority = Medium) or (A.Priority = High) then
|>     ...
|>   end if;
|>   if (A.Priority = High) then
|>     Display...
|>     Set_Alarm...
|>   end if;
|>
|> This is trivial to understand and maintain, and the time and
|> space overhead is minimal.

I agree that the example in Section I-2.3 of the 9X Rationale is
peculiar.  It has a tall, thin inheritance hierarchy
(with branching factor 1): 

   Base_Alert
       |
    Low_Alert
       |
  Medium_Alert
       |
   High_Alert

Technically, this IS equivalent to an Ada-83 variant type, but a rather
ugly one: 

   type Alert_Tag is
      (Base_Alert, Normal_Alert, Medium_Alert, High_Alert);

   type Alert (Tag: Alert_Tag) is
      record
         case Tag is
            when Base_Alert =>
               null;
            when Normal_Alert .. High_Alert =>
               Time_Of_Arrival : Calendar.Time;
               Message         : Text;
               case Tag is
                  when Medium_Alert .. High_Alert =>
                     Action_Officer: Person;
                     case Tag is
                        when High_Alert =>
                           Ring_Alarm_At: Calendar.Time;
                        when others =>
                           null;
                     end case;
                  when others =>
                     null;
               end case;
         end case;
      end record;

However, I think the flattened alternative Moti proposes is far worse.
I've maintained code (not written in Ada) that uses "implicit variants"
of this sort, and it's a nightmare.  The code provides no clear clue that
a structure comes in a number of forms, that certain components determine
which form a given instance of the structure holds, and that certain
components only have meaningful values in certain forms.  As the program
was modified over the years by people who did not understand the implicit
variant structure as well as the original programmers understood it, we
find that code to copy an instance of the structure would copy even the
currently inactive components, to be on the safe side.  We find bugs that
were introduced by programmers apparently assuming that since the
structure appeared to be initialized, all of its components must have
meaningful values.  These bugs were repaired by fixing the most proximate
symptom--stuffing a value into a component that belongs to a logically
inactive variant, thus destroying the original programmers' notion of
distinct variants and creating a much more complicated set of rules about
when components have meaningful values.  Inevitably, programmers needing
to add more components to the structure for use during a particular phase
of the program noticed that certain components were unused during that
phase of the program, and decided that rather than waste storage, they
would "reuse" the existing component, using the same component (or, worse
yet, a differently typed component overlaid with that component) for a
completely different purpose.  This made it great fun trying to figure
out what kind of data was held in a particular component at a particular
point in the program.

It would have been much better if the original programmer had written an
Ada record with a variant part.  A particular component of the record
would have been clearly identified as a discriminant, it would have
been clearly documented that the record has a finite number of forms, and
the variant part would clearly have indicated which components are
present in which forms.  A maintenance programmer trying to reference a
component in an inactive variant would have been alerted by a
Constraint_Error (or perhaps even a compile-time warning) that he was
doing something fundamentally meaningless, and would have redesigned his
modifications accordingly rather than breaking the original
data-structure design.  The compiler would automatically take care of
storage mapping, overlaying a new component for one variant with storage
used for other components in other variants, while allowing the
programmer to remain oblivious to the overlay and preserving the logical
view that the new component is distinct from all other components.

I short, I am firmly convinced that explicit variants controlled by
discriminants are a signficant advance over flat records in which the
current validity of each component is controlled by programmer
convention.

Returning to Moti's request for a more convincing example of type
extension, consider streams, introduced Section 13.13 on RM9X.
Ada.Streams.Root_Stream_Type is an abstract type with Read and Write
operations.  Streams are, from an abstract point of view, sinks and
sources of raw binary data.  Among the possible "variants" of streams are
streams implemented as files, streams implemented as in-memory buffers,
and streams implemented as communications ports.  Packages
Ada.Streams.Stream_IO and Ada.Text_IO.Text_Streams provide functions that
offer access to a stream corresponding to an open file; although the
implementation of these streams is not specified by the RM, a stream
implemented as a file seems to be the only reasonable choice.  The
Distributed Systems Annex package System.RPC defines an extension of
Root_Stream_Type named Params_Stream_Type, most likely implemented as a
memory buffer or, on a distributed system without shared memory, as a
communications port.

The strength of the OOP approach for streams lies in the fact that one
can write subprograms with Root_Stream_Type'Class parameters using only
the abstract Read and Write operations of streams, without regard to how
streams are implemented.  (Most notable among these are the T'Read and
T'Write subprograms for translating complex data structures, perhaps
containing access values, to and from streams of bytes whose meaning is
independent of the addresses implied by the access values.)  Furthermore,
if other implementations of streams are later conceived, new extensions
of stream types can be declared with versions of Read and Write that are
implemented accordingly.  All software previously designed to work with
streams will then work with the new kind of stream.  Calls on Read and
Write from within this software will automatically be "dispatched" to the
versions of these procedures for the "variant" of streams currently being
manipulated.

Tagged types are, in effect, like Ada-83 types with variants in which the
case statements examining discriminants are implicit in dispatching calls
and in which new variants can be added without modifying old source.

--
Norman H. Cohen    ncohen@watson.ibm.com



      parent reply	other threads:[~1994-11-21 15:33 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1994-11-18 13:16 Motivating inheritance and dyn. poly Moti Ben-Ari
1994-11-19  1:48 ` Bob Duff
1994-11-21 15:33 ` Norman H. Cohen [this message]
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox