comp.lang.ada
 help / color / mirror / Atom feed
* Motivating inheritance and dyn. poly.
@ 1994-11-18 13:16 Moti Ben-Ari
  1994-11-19  1:48 ` Bob Duff
  1994-11-21 15:33 ` Norman H. Cohen
  0 siblings, 2 replies; 3+ messages in thread
From: Moti Ben-Ari @ 1994-11-18 13:16 UTC (permalink / raw)


I am writing a textbook which will have a chapter
on object-oriented programming (I suppose all textbooks
these days have to have a chapter on OOP...).

My problem is that I have yet to see a truly convincing
example of the need for inheritance and dynamic polymorphism
in a real application.
Can anyone contribute a good example of an application
that is significantly better with inheritance?

To analyze in more detail:
The 9X rationale talks about variant programming
and class-wide programming.
As for class-wide programming, most of the examples seem to deal
with heterogeneous data structures, whose problems are caused
by strong-typing.
If that is the justification for inheritance, then it becomes
a technical matter hardly of interest to the average programmer.

I would like to go more deeply into variant programming, as type
extension seems to be the real justification of inheritance.
One phenomenon that I repeatly see is "variant overkill",
i.e. using variants for no essential purpose.
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.

It is _true_ that adding an alarm of a new priority
will require significant modification of the package specification.
But I believe that a more likely scenario is that the day
after you deliver the system you will get a call:

  "My operators are falling asleep at the controls of the reactor;
   I want an alarm raised in all priorities."

In a non-variant, non-tagged implementation, the modification
is a trivial modification __within__ the package body:

  if (A.Priority = High) then
    Display...
  end if;
  Set_Alarm...

With a tagged implementation, this trivial request breaks the
entire type hierarchy and requires non-trivial modification
of the type definitions.

I don't want to put myself against the whole world,
and object to object-oriented programming (no pun intended!).
I would like to ask for help in motivating the use of inheritance
in the design and programming of a true application:
not insects or rectangles, but air-traffic control,
financial market modelling, cellular phone switching --
the sort of things we claim that OOP in general and Ada
in particular is good for.

Thanks

Moti

---------------------------------------
Dr. Moti Ben-Ari, Mavo Software Ltd.
benari@datasrv.co.il
POB 1603, Rehovot 76115, Israel
Tel: 972-8-470-793, Fax: 972-8-466-831
---------------------------------------






^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Motivating inheritance and dyn. poly.
  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
  1 sibling, 0 replies; 3+ messages in thread
From: Bob Duff @ 1994-11-19  1:48 UTC (permalink / raw)


In article <3ai9g8$5e6@israel-info.datasrv.co.il>,
Moti Ben-Ari <benari@zeus.datasrv.co.il> wrote:
>As for class-wide programming, most of the examples seem to deal
>with heterogeneous data structures, whose problems are caused
>by strong-typing.
>If that is the justification for inheritance, then it becomes
>a technical matter hardly of interest to the average programmer.

I don't understand that.  Why is strong typing "hardly of interest"?
Please explain.

- Bob
-- 
Bob Duff                                bobduff@inmet.com
Oak Tree Software, Inc.
Ada 9X Mapping/Revision Team (Intermetrics, Inc.)



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Motivating inheritance and dyn. poly.
  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
  1 sibling, 0 replies; 3+ messages in thread
From: Norman H. Cohen @ 1994-11-21 15:33 UTC (permalink / raw)


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



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~1994-11-21 15:33 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox