comp.lang.ada
 help / color / mirror / Atom feed
* Ada 90 inheritance request?
@ 1994-11-23 21:33 S M Ryan
  1994-12-02 16:46 ` Tucker Taft
  0 siblings, 1 reply; 13+ messages in thread
From: S M Ryan @ 1994-11-23 21:33 UTC (permalink / raw)


A few months back, there were some postings comparing
Ada90 proposed single inheritance and how it compared
to multiple inheritance such as C. Does any kind soul
still have copies?

-- 
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ __________________________________
| | | | | | | | | | | | | | | | | | | | | smryan@netcom.com	PO Box 1563
| | | | | | | | | | | | | | | | | | | | |             Cupertino, California
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_(xxx)xxx-xxxx_______________95015



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

* Re: Ada 90 inheritance request?
  1994-11-23 21:33 Ada 90 inheritance request? S M Ryan
@ 1994-12-02 16:46 ` Tucker Taft
  1994-12-09 17:26   ` Cyrille Comar
  0 siblings, 1 reply; 13+ messages in thread
From: Tucker Taft @ 1994-12-02 16:46 UTC (permalink / raw)



Here is the note on multiple inheritance in Ada 9X.
-Tucker Taft

P.S. This note is absolutely not intended to start 
Y.A.F.W. (yet another flame war ;-). -TT

-------------------------
                       MULTIPLE INHERITANCE IN ADA 9X

                        S. Tucker Taft stt@inmet.com
                        Ada 9X Mapping/Revision Team
			     Copyright (C) 1994
                             Intermetrics, Inc.
                             733 Concord Avenue
                            Cambridge, MA 02138
		 May be copied if accompanied by this notice.

This note discusses the creation of multiple inheritance type hierarchies
using the Ada 9X object-oriented programming features.  It is in part
directed at programmers familiar with other object-oriented programming
languages that build in syntax for a particular multiple-inheritance
mechanism, rather than simply providing the building blocks needed to
support it.

In this discussion, we will in general use Ada 9X terminology, where every
object has a single "type," and multiple similar types (typically in some
kind of hierarchy or oligarchy) form a "class" of types.  If we want to use
the term "class" as it is used in C++ or Eiffel, we will always say "C++
class" or "Eiffel class."

In some languages, such as Eiffel, multiple inheritance serves many
purposes.  For example, there is no equivalent in Eiffel to the "include"
statement of C/C++ or the "with/use" clauses of Ada.  Therefore, to gain
direct visibility to the declarations of some other module, one must inherit
from that module (Eiffel class).

In C/C++, one can simply "include" a file containing a set of type and
object definitions.

In Ada, one first identifies the external modules of interest via "with"
clauses, and then chooses selectively whether to make only the name of the
module (package) visible, or its contents (via a "use" clause).

In both Eiffel and C++, one can choose to inherit from some other type,
without making that visible to clients of the new type.  Effectively, the
linguistic multiple inheritance mechanism is being used to express not an
"is a refinement of" relationship, but rather an "is implemented using"
relationship.

Finally, there are the situations where a single type visibly inherits from
two or more other types.  In these cases, this is rarely a symmetric
situation.  Rather, one of the ancestor types is the "primary" ancestor,
while the others are typically "mix-ins" designed to augment behavior of the
primary ancestor.

Ada 9X supports multiple-inheritance module inclusion (via multiple
"with"/"use" clauses), multiple-inheritance "is-implemented-using" via
private extensions and record composition, and multiple-inheritance mix-ins
via the use of generics, formal packages, and access discriminants.

The Ada 9X mechanisms are designed to eliminate "distributed" overhead, so
that there is no added expense for the general user of the language because
of the presence of the mechanisms supporting multiple inheritance.
Furthermore, the Ada 9X building blocks that support multiple inheritance
are general purpose, and support many other paradigms beyond conventional
multiple inheritance.

There are basically three distinct situations associated with multi-
inheritance mixins:

   1. The case where the mix-in provides components and operations, and
      any overriding of these operations needs only to look at the
      components of the mix-in itself.

   2. The case where the mix-in provides components and operations, and
      some of the overriding of these operations needs access to the
      whole object, rather than just the components of the mix-in.

   3. Like (2), and in addition, any object with the mix-in must be
      able to be linked onto a list (or into some similar heterogeneous
      data structure) of other objects with the same mix-in.

Case (1) is supported directly in Ada 9X by a record or private extension,
with the type being mixed in (in a possibly extended form) as a component of
the record extension.

Case (2) is handled with a generic, that takes any type in a given class
(formal derived type), adds components (via extension) and operations, and
then reexports the extended type.  The new operations have access to the
whole object, not just to the components being added.

Case (3) is handled with an access discriminant, that provides access to the
enclosing object for the operations of the mix-in, while still allowing
links through the mix-in.  Generics can also be used to automate the
approach.

Here are a few examples:

   - Case (1) -- One has an abstract type "Set_of_Strings" and one
     wants to implement it by reusing an existing (concrete) type
     "Hash_Table":

     Here is the abstract type:

           type Set_Of_Strings is abstract tagged limited private;
           type Element_Index is new Natural;  -- Index within set.

           No_Element : constant Element_Index := 0;

           Invalid_Index : exception;

           procedure Enter(
             -- Enter an element into the set, return the index
              Set : in out Set_Of_Strings;
              S : String;
              Index : out Element_Index) is abstract;

           procedure Remove(
             -- Remove an element from the set; ignore if not there
              Set : in out Set_Of_Strings;
              S : String) is abstract;

           procedure Combine(
             -- Combine Additional_Set into Union_Set
              Union_Set : in out Set_Of_Strings;
              Additional_Set : Set_Of_Strings) is abstract;

           procedure Intersect(
             -- Remove all elements of Removal_Set from Intersection_Set
              Intersection_Set : in out Set_Of_Strings;
              Removal_Set : Set_Of_Strings) is abstract;

           function Size(Set : Set_Of_Strings) return Element_Index 
             is abstract;
             -- Return a count of the number of elements in the set

           function Index(
             -- Return the index of a given element;
             -- return No_Element if not there.
              Set : Set_Of_Strings;
              S : String) return Element_Index is abstract;

           function Element(Index : Element_Index) return String is abstract;
             -- Return element at given index position
             -- raise Invalid_Index if no element there.
         private
           type Set_Of_Strings is abstract tagged limited ...


     Here is an implementation of this abstract type that inherits its
     interface from Set_Of_Strings, and its implementation from
     Hash_Table:

           type Hashed_Set(Table_Size : Positive) is
             new Set_Of_Strings with private;

           -- Now we give the specs of the operations being implemented
           procedure Enter(
             -- Enter an element into the set, return the index
              Set : in out Hashed_Set;
              S : String;
              Index : out Element_Index);

           procedure Remove(
             -- Remove an element from the set; ignore if not there
              Set : in out Hashed_Set;
              S : String);
           . . . etc.

         private
           type Hashed_Set(Table_Size : Positive) is
             new Set_Of_Strings with record
               Table : Hash_Table(1..Table_Size);
             end record;

     In the body of this package, we would provide the bodies for each
     of the operations, in terms of the operations available from
     Hash_Table.  Chances are they don't match exactly, so a little bit
     of "glue" code will be necessary in any case.

   - Case (2) -- One has a type Basic_Window that responds to various
     events and calls.  One wants to embellish the Basic_Window in
     various ways with various mix-ins.

          type Basic_Window is tagged limited private;
          procedure Display(W : Basic_Window);
          procedure Mouse_Click(W : in out Basic_Window;
                                Where : Mouse_Coords);
          . . .


     Now one can define any number of mix-in generics, like the
     following:

          generic
            type Some_Window is new Window with private;
              -- take in any descendant of Window
          package Label_Mixin is
            type Window_With_Label is new Some_Window with private;
              -- Jazz it up somehow.

            -- Overridden operations:
            procedure Display(W : Window_With_Label);

            -- New operations:
            procedure Set_Label(W : in out Window_With_Label; S : String);
              -- Set the label
            function Label(W : Window_With_Label) return String;
              -- Fetch the label
          private
            type Window_With_Label is
              new Some_Window with record
                Label : String_Quark := Null_Quark;
                  -- An XWindows-Like unique ID for a string
              end record;

     In the generic body, we implement the Overridden and New
     operations as appropriate, using any inherited operations, if
     necessary.  For example, this might be an implementation of the
     overridden Display:

          procedure Display(W : Window_With_Label) is
          begin
              Display(Some_Window(W));
                -- First display the window normally,
                -- by passing the buck to the parent type.

              if W.Label /= Null_Quark then
                -- Now display the label if it is not null
                  Display_On_Screen(XCoord(W), YCoord(W)-5, Value(W.Label));
                    -- Use two inherited functions on Basic_Window
                    -- to get the coordinates where to display the label.
              end if;
          end Display;

     Presuming we have several such generic packages defined, We can
     now create the desired window by mixing in repeatedly.  First we
     declare the tailored window as a private extension of
     Basic_Window, and then we define it via a sequence of
     instantiations:

            type My_Window is Basic_Window with private;
            . . .
          private
            package Add_Label is new Label_Mixin(Basic_Window);
            package Add_Border is
              new Border_Mixin(Add_Label.Window_With_Label);
            package Add_Menu_Bar is
              new Menu_Bar_Mixin(Add_Border.Window_With_Border);

            type My_Window is
              new Add_Menu_Bar.Window_With_Menu_Bar with null record;
                -- Final window is a null extension of Window_With_Menu_Bar.
                -- We could instead make a record extension and
                -- add components for My_Window over and above those
                -- needed by the mix-ins.

   - Case(3) -- In this case, let us presume we have two independent
     hierarchies, one for Windows, which represent areas on the screen,
     and one for Monitors, which wait for an object to change, and then
     do something in reaction to it.  An object that supports Monitors
     keeps a linked list of Monitors, and calls their Update operation
     whenever the object is changed.  For example:

          type Monitor;
          type Monitor_Ptr is access Monitor'CLASS;

          type Monitored_Object is abstract tagged limited
            -- Monitored objects are derived from this root type
            record
              First : Monitor_Ptr;  - List of monitors
              -- More components to be added by extension
            end record;

          type Monitored_Object_Ptr is access Monitored_Object'CLASS;

          type Monitor is abstract tagged limited
            record
              Next : Monitor_Ptr;
              Obj : Monitored_Object_Ptr;
              -- More components to be added by extension
            end record;
          procedure Update(M : in out Monitor) is abstract;
            -- Dispatching operation, called when monitored object
            -- is changed.

     Now suppose we want to create a Window that can act as a Monitor
     as well as a Window:

     First we define a mix-in that is a monitor, and override its
     Update op:

          type Monitor_Mixin(Win : access Basic_Window'CLASS) is
            new Monitor with null record;
          procedure Update(M : in out Monitor_Mixin);

     The body for this Update could be:

          procedure Update(M : in out Monitor_Mixin) is
            -- On an Update, simply re-Display the window
          begin
              Display(M.Win);  -- This is a dispatching call
          end Update;

     Now we can "mix" this Monitor_Mixin into any window type, as
     follows:

          type Window_That_Monitors is
            new My_Window with record
              Mon : Monitor_Mixin(Window_That_Monitors'ACCESS);
            end record;


     We could define a tailored Monitor mix-in that did something
     besides just call the Display operation of the associated Window.
     But in many cases, this simple one will do the job.

As these examples illustrate, Ada 9X provides support for the construction
of essentially arbitrary multiple inheritance type hierarchies, without
having to commit itself to a single linguistic mechanism for multiple
inheritance, and without burdening simple single-inheritance problems with
the complexity and implementation burden of linguistic multiple inheritance.

Furthermore, the building blocks that support multiple inheritance are very
general, and allow the programmer to easily go well beyond the limitations
of conventional multiple inheritance mechanisms.



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

* Re: Ada 90 inheritance request?
  1994-12-02 16:46 ` Tucker Taft
@ 1994-12-09 17:26   ` Cyrille Comar
  1994-12-11 18:47     ` Bob Duff
                       ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Cyrille Comar @ 1994-12-09 17:26 UTC (permalink / raw)


stt@spock.camb.inmet.com (Tucker Taft) writes:
: 
: Here is the note on multiple inheritance in Ada 9X.
: -Tucker Taft

Thank's Tuck for your very instructive note. I love this kind of post where I
fill like learning something rather than listening to someone complains.
By the way, I have an intersting challenge for our Ada9x experts. Here is a
problem that would have an obvious solution with Multiple Inheritance, I would
like to see how it can be solved cleanly in 9x. The problem is not abstract,
this is something that everybody will need at some point.

PROBLEM: I have defined a hierarchy or tagged types and now I would like
         specialize one of them to be controlled (i.e. finalizable)

with MI, I could write something like:
   type Ctrl_T is new T, Controlled with null record;
   -- overriding of Initialize/Adjust/Finalize
   procedure Initialize (Obj : in out Ctrl_T);
   procedure Adjust     (Obj : in out Ctrl_T);
   procedure Finalize   (Obj : in out Ctrl_T);
and it would be the end of it...

Beginning of Solution
---------------------
Following your suggestions, we could define a generic package:

generic 
   type T is tagged private;
package Make_it_Controlled is

   type Ctrl_T is new T with private;
   procedure Initialize (Obj : in out Ctrl_T);
   procedure Adjust     (Obj : in out Ctrl_T);
   procedure Finalize   (Obj : in out Ctrl_T);

private

   type T_Controller is new Controlled with null record;
   procedure Initialize (Obj : in out T_Controller);
   procedure Adjust     (Obj : in out T_Controller);
   procedure Finalize   (Obj : in out T_Controller);

   type Ctrl_T is new T with record
      Ctrl : T_Controller;
   end record;
end;

and then I need to be able to call Initialize on Ctrl_T inside the Initialize
of type T_Controller. Each time a variable V of type Ctrl_T is defined
the Initialize on V.Ctrl will be called and will itself call Initialize on V.
But now I am stuck because there is no way in the body of Initialize on
T_Controller to refer to the englobing object.... Argggg

Tuck gave another nice trick by using access discriminant that solves partially
the problem. We can use it to redefine the controller:

   type Acc is access all Ctrl_T;
   type T_Controller (Englobing_Obj : Acc) is new Limited_Controlled 
     with null record;

   type Ctrl_T is new T with record
      Ctrl : T_Controller (Ctrl_T'Access);
   end;

and now I can write Initialize 

   procedure Initialize (Obj : in out T_Controller) is
   begin
      Initialize (Obj.Englobing_Obj.all);
   end;

And everything would be nice if it was legal... The problem is that the access
discriminant requires a LIMITED TYPE and thus Ctrl_T must be limited.
So this approach works pretty well if my formal generic parameter is 
   type T is tagged limited private;
 This package allows any limited tagged type to be specialized as a
limited_controlled type (sort of).

MY CHALLENGE is : how to do the same thing with non-limited types ?
-- 
------------------------------------------------------------------------
Cyrille Comar,                                  E-mail: comar@cs.nyu.edu
Gnat Project                                    US phone: (212) 998-3489




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

* Re: Ada 90 inheritance request?
  1994-12-09 17:26   ` Cyrille Comar
@ 1994-12-11 18:47     ` Bob Duff
  1994-12-12  3:15     ` Tucker Taft
  1994-12-13 19:02     ` John Goodsen
  2 siblings, 0 replies; 13+ messages in thread
From: Bob Duff @ 1994-12-11 18:47 UTC (permalink / raw)


In article <3ca3vl$n14@lang8.cs.nyu.edu>,
Cyrille Comar <comar@cs.nyu.edu> wrote:
>PROBLEM: I have defined a hierarchy or tagged types and now I would like
>         specialize one of them to be controlled (i.e. finalizable)

This issue came up in the review process, and I'll repeat pretty much
what we said at the time.

I think you have to ask what are the Initialize and Finalize *doing*?

In most cases, you want to add controlledness because you are adding a
component that needs it.  For example, you are extending the parent type
by adding a component of an access type, and you want finalization to
clean up the heap object.  In this (most common) case, it's no big deal
-- just make the new component be of a controlled type.  Whenever an
object of the derived type is finalized, all of its components will be
finalized.  In this case, Initialize and Finalize don't need access to
the containing object.

It's hard to imagine a case where Initialize and Finalize for the new
component(s) *do* need access to the containing object.  Can anybody
think of a realistic one?  I would be interested to hear it.

I suppose if the parent type needs finalization, but doesn't have it,
then one might want to add it to avoid storage leaks or whatever.  But
in that case, the parent type is already broken.  Type extension is
mainly for extending abstractions, not really for fixing broken ones --
the latter is what Emacs is for.  ;-)

If the Initialize and Finalize *do* need access to the parent's fields,
then the access-discriminant method can be used.  But, as you point out,
it only works for limited types.

By the way, in your example:

>   type Acc is access all Ctrl_T;
>   type T_Controller (Englobing_Obj : Acc) is new Limited_Controlled 
>     with null record;
>
>   type Ctrl_T is new T with record
>      Ctrl : T_Controller (Ctrl_T'Access);
>   end;

Englobing_Obj is *not* an access discriminant.  It is a discriminant,
and it is of a *named* access type.  An "access discriminant" is a
discriminant of an *anonymous* access type.  (The terminology is a bit
confusing, I admit.)

As you wrote it, the example would be illegal, because it violates the
accessibility rules (i.e. it can create dangling references).  You could
make it legal by changing 'Access to 'Unchecked_Access, but that leaves
the possibility of dangling references (somebody might copy the value of
the discriminant into a global variable of type Acc).  Better to use an
access discriminant:

    type T_Controller(Englobing_Obj: access Ctrl_T) is ...

This prevents dangling references, and is legal.

By the way, the "limited" rule you referred to is that only a limited
type can have an access discriminant.  Any type (limited or nonlimited)
can have a discriminant of a named access type.  (Well, it has to be
composite, and not an array.)

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



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

* Re: Ada 90 inheritance request?
  1994-12-09 17:26   ` Cyrille Comar
  1994-12-11 18:47     ` Bob Duff
@ 1994-12-12  3:15     ` Tucker Taft
  1994-12-13 19:02     ` John Goodsen
  2 siblings, 0 replies; 13+ messages in thread
From: Tucker Taft @ 1994-12-12  3:15 UTC (permalink / raw)


In article <3ca3vl$n14@lang8.cs.nyu.edu>,
Cyrille Comar <comar@cs.nyu.edu> wrote:

> ...
>PROBLEM: I have defined a hierarchy or tagged types and now I would like
>         specialize one of them to be controlled (i.e. finalizable)

[Later you impose the additional requirement that the type be non-limited.]

One cop-out solution is to make the root type controlled (even though
OOP allows one to minimize the need to edit code when reusing it,
it doesn't eliminate it altogether).  Although this may seem be a bit of
a cop-out, it may be that if one descendant of a type wants to "add" 
finalization, then other descendants would also benefit from it.

Another approach is to simply make the new *components* 
controlled, rather than the type as a whole.  In general, 
I would recommend that components clean up "themselves," rather than 
rely on a clean-up routine of an enclosing type.  It seems to be
simpler and more flexible.  

To give the best answer, it would help to have a better idea of
what is the underlying problem you want to solve.  In the abstract
it is hard to make appropriate tradeoffs.  For example, it might be a 
sign that you are abusing the "Is-A" relationship if a descendant 
needs to finalize the object as a whole (as opposed to just its 
new components) while its ancestor types do not.  This might imply
that inheritance is the wrong approach to begin with, and some
other kind of type composition would fit the problem better.  E.g.,
perhaps you should make the planned parent type into a component
of your new type, rather than making the new type an extension of it.

More details would help...

>Cyrille Comar,                                  E-mail: comar@cs.nyu.edu
>Gnat Project                                    US phone: (212) 998-3489

-Tucker Taft  stt@inmet.com
Intermetrics, Inc.  



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

* Re: Ada 90 inheritance request?
  1994-12-09 17:26   ` Cyrille Comar
  1994-12-11 18:47     ` Bob Duff
  1994-12-12  3:15     ` Tucker Taft
@ 1994-12-13 19:02     ` John Goodsen
  1994-12-14 19:49       ` John Goodsen
  1994-12-17 13:55       ` Tucker Taft
  2 siblings, 2 replies; 13+ messages in thread
From: John Goodsen @ 1994-12-13 19:02 UTC (permalink / raw)


In article <3ca3vl$n14@lang8.cs.nyu.edu> comar@cs.nyu.edu (Cyrille Comar) writes:

I am dismayed at how anyone could look at this so-called "solution" to
multiple inheritance and walk away comfortable that Ada9X properly
supports MI.  The lack of *direct* support for MI in Ada 9X, is IMHO,
a serious foobar that has to this point been explained away by the
ivory tower language designers as either (a) something that I don't
really need (thanks for being so luminiscent in my needs) or (b) if
you really need it, it's only for abstract, mixin type class behavior
which you can perform using SI and generics.  Neither of which is an
aesthetically or technically pleasing solution.  So I'll go past the
rhetoric of verbal, get-us-nowhere discussions on why *DIRECT* support
for MI should or should not be in Ada 9X and propose a little
excercise for one of those language designers who feel that direct MI
support is not necessary:

   1. Read and understand the Design Patterns book by Gamma et. al...
      If you haven't read this yet, then you are missing out on some
      great insight into designing reusable software.

   2. Recreate in Ada9X some of the sample implementations of patterns that
      require MI as part of the solution.  Post the Gamma et. al. C++ solutions
      and compare them to the Ada9X solution.  I believe it will become
      quite obvious what a hack it turns out to be in Ada 9X.

Tucker, sound like something you or your people could take on as a quick
and dirty excercise?  I think it would give you better insight into the problems.
you up for it?

speaking from my Wall St. cubicle...

--
John Goodsen                         Currently on-site at:
The Dalmatian Group                       JP Morgan 
User Interface Specialists                60 Wall St., New York City
jgoodsen@radsoft.com                      jgoodsen@jpmorgan.com

--- included message ----

   stt@spock.camb.inmet.com (Tucker Taft) writes:
   : 
   : Here is the note on multiple inheritance in Ada 9X.
   : -Tucker Taft

   Thank's Tuck for your very instructive note. I love this kind of post where I
   fill like learning something rather than listening to someone complains.
   By the way, I have an intersting challenge for our Ada9x experts. Here is a
   problem that would have an obvious solution with Multiple Inheritance, I would
   like to see how it can be solved cleanly in 9x. The problem is not abstract,
   this is something that everybody will need at some point.

   PROBLEM: I have defined a hierarchy or tagged types and now I would like
	    specialize one of them to be controlled (i.e. finalizable)

   with MI, I could write something like:
      type Ctrl_T is new T, Controlled with null record;
      -- overriding of Initialize/Adjust/Finalize
      procedure Initialize (Obj : in out Ctrl_T);
      procedure Adjust     (Obj : in out Ctrl_T);
      procedure Finalize   (Obj : in out Ctrl_T);
   and it would be the end of it...

   Beginning of Solution
   ---------------------
   Following your suggestions, we could define a generic package:

   generic 
      type T is tagged private;
   package Make_it_Controlled is

      type Ctrl_T is new T with private;
      procedure Initialize (Obj : in out Ctrl_T);
      procedure Adjust     (Obj : in out Ctrl_T);
      procedure Finalize   (Obj : in out Ctrl_T);

   private

      type T_Controller is new Controlled with null record;
      procedure Initialize (Obj : in out T_Controller);
      procedure Adjust     (Obj : in out T_Controller);
      procedure Finalize   (Obj : in out T_Controller);

      type Ctrl_T is new T with record
	 Ctrl : T_Controller;
      end record;
   end;

   and then I need to be able to call Initialize on Ctrl_T inside the Initialize
   of type T_Controller. Each time a variable V of type Ctrl_T is defined
   the Initialize on V.Ctrl will be called and will itself call Initialize on V.
   But now I am stuck because there is no way in the body of Initialize on
   T_Controller to refer to the englobing object.... Argggg

   Tuck gave another nice trick by using access discriminant that solves partially
   the problem. We can use it to redefine the controller:

      type Acc is access all Ctrl_T;
      type T_Controller (Englobing_Obj : Acc) is new Limited_Controlled 
	with null record;

      type Ctrl_T is new T with record
	 Ctrl : T_Controller (Ctrl_T'Access);
      end;

   and now I can write Initialize 

      procedure Initialize (Obj : in out T_Controller) is
      begin
	 Initialize (Obj.Englobing_Obj.all);
      end;

   And everything would be nice if it was legal... The problem is that the access
   discriminant requires a LIMITED TYPE and thus Ctrl_T must be limited.
   So this approach works pretty well if my formal generic parameter is 
      type T is tagged limited private;
    This package allows any limited tagged type to be specialized as a
   limited_controlled type (sort of).

   MY CHALLENGE is : how to do the same thing with non-limited types ?
   -- 
   ------------------------------------------------------------------------
   Cyrille Comar,                                  E-mail: comar@cs.nyu.edu
   Gnat Project                                    US phone: (212) 998-3489

-- 
--
John Goodsen                         Currently on-site at:
The Dalmatian Group                       JP Morgan 
User Interface Specialists                60 Wall St., New York City
jgoodsen@radsoft.com                      jgoodsen@jpmorgan.com



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

* Re: Ada 90 inheritance request?
  1994-12-13 19:02     ` John Goodsen
@ 1994-12-14 19:49       ` John Goodsen
  1994-12-15 18:41         ` Robert I. Eachus
  1994-12-17 13:55       ` Tucker Taft
  1 sibling, 1 reply; 13+ messages in thread
From: John Goodsen @ 1994-12-14 19:49 UTC (permalink / raw)


In article <JGOODSEN.94Dec13140247@treasure.radsoft.com> jgoodsen@treasure.radsoft.com (John Goodsen) writes:

   Tucker, sound like something you or your people could take on as a quick
   and dirty excercise?  I think it would give you better insight into the problems.
   you up for it?

Of course, I don't really expect the ivory tower league to undertake
such a revealing comparison.  It makes too much sense and would expose
the err in the MI reasoning process.  Keeping your balance on the soapbox
seems to take priority over putting Ada into the mainstream communities.

Ahhhh, back to the real world of C++ (unfortunately).  You guys make
it too damned hard to sell Ada into the mainstream markets...

see ya 6 feet under in the OOPL graveyard...
-- 
--
John Goodsen                         Currently on-site at:
The Dalmatian Group                       JP Morgan 
User Interface Specialists                60 Wall St., New York City
jgoodsen@radsoft.com                      jgoodsen@jpmorgan.com



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

* Re: Ada 90 inheritance request?
  1994-12-14 19:49       ` John Goodsen
@ 1994-12-15 18:41         ` Robert I. Eachus
  1994-12-19 18:58           ` John Goodsen
  0 siblings, 1 reply; 13+ messages in thread
From: Robert I. Eachus @ 1994-12-15 18:41 UTC (permalink / raw)


In article <JGOODSEN.94Dec14144949@treasure.radsoft.com> jgoodsen@treasure.radsoft.com (John Goodsen) writes:

  > Of course, I don't really expect the ivory tower league to
  > undertake such a revealing comparison.  It makes too much sense
  > and would expose the err in the MI reasoning process.  Keeping
  > your balance on the soapbox seems to take priority over putting
  > Ada into the mainstream communities.

   Never being afraid to jump into the fray, I'll respond to John by
taking his MI example and asking why the obvious Ada 95 version is not
acceptable: 

    type Needs_Finalization is new Controlled with...;
    -- overriding of Initialize/Adjust/Finalize
    procedure Initialize (Obj : in out Needs_Finalization);
    procedure Adjust     (Obj : in out Needs_Finalization);
    procedure Finalize   (Obj : in out Needs_Finalization);

    type Ctrl_T is new T with record
      NF:  Needs_Finalization;
    end record;

    Now I can think of cases where this won't work, but they are
precisely the cases where the "true" MI version won't work either.
No, I take that back.  There are cases where true MI will fail, and
the Ada 95 version will sail through.  For example, if someone
modifies T so that it inherits directly from Controlled...

    In general where Ada 95 says "this is unsafe, so we will not allow
it," most MI languages say "this is really neat, and if you are VERY
careful, it will work, but never on large projects, because it doesn't
scale well."

     I've been there, I've done that, I've seen the maintenance
nightmares that can occur when more than one programmer depends on
unsafe MI on the same project.

     There is a lot of reasonable, and safe, MI out there.  But if you
check, it is all easily (and cleanly) translatable to Ada 95.  In this
case, I think it was Tucker who gave exactly the right answer--if
Ctrl_T needs to modify parts of T as part of assignment or
finalization, you really need to think long and hard about whether or
not other descendants of T will need to do the same thing.  If so, the
right choice is to make T a (direct or indirect) child of Controlled.
If not then you are almost certainly breaking the class model for T.
(Making Ctrl_T the root of it's own class, or the part of a class
rooted at Controlled, and having it add the "T" abstraction as a
component or mix-in is easy, if a solution without the "is a" property
is correct.)

--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...



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

* Re: Ada 90 inheritance request?
  1994-12-13 19:02     ` John Goodsen
  1994-12-14 19:49       ` John Goodsen
@ 1994-12-17 13:55       ` Tucker Taft
  1 sibling, 0 replies; 13+ messages in thread
From: Tucker Taft @ 1994-12-17 13:55 UTC (permalink / raw)


In article <JGOODSEN.94Dec13140247@treasure.radsoft.com>,
John Goodsen <jgoodsen@treasure.radsoft.com> wrote:

>I am dismayed at how anyone could look at this so-called "solution" to
>multiple inheritance and walk away comfortable that Ada9X properly
>supports MI.  The lack of *direct* support for MI in Ada 9X, is IMHO,
>a serious foobar that has to this point been explained away by the
>ivory tower language designers ...

Feel free to accuse of us of all kinds of crimes against nature,
but please don't put us in an ivory tower ;-).  I trust that those
who know me recognize that I tend to live more in the cement dungeon
of language design, scrabbling around in the cement dust of human
engineering, implementation concerns, efficiency, etc.  
The Ada 9X mapping/revision team consisted almost entirely of dedicated, very 
experienced, Ada and other-language programmers with a strong penchant 
for designing the most productive, reliable, readable, and usable language 
they knew how, given the constraints of the process.

An easy to use, intuitive, efficient, straightforward, multiple inheritance
mechanism in Ada 9X would have been great.  Unfortunately, we have never
seen such a beast.  Perhaps you have.  What we found when investigating
ways to support multiple inheritance was that everybody knew what was
the best way to do it -- their own way!  When we have encountered such
things in the past during language design, the conclusion to be drawn
is that implementing multiple inheritance is still a *programming* problem,
not a language design problem.  If even two existing OOP languages did
Multiple Inheritance the same way we would have seen that as a reasonable
starting place.  But the fact is that the rules for dealing with the
inevitable additional complexities of multiple inheritance are different
in essentially every langauge we looked at.  Even Sather and Eiffel, which
otherwise have a lot in common, differ in how they resolve the MI issues.
C++ in its usual pragmatic way provides two different ways to do MI,
using regular or virtual base classes.  CLOS just merges based on names
and a search order for methods.  Eiffel provides renaming.  Etc...

By constrast, all of the languages agree on how to do single inheritance
in the language, and it is essentially unchanged since the days of 
Simula '67.  It is intuitive, efficient, straightforward, reliable,
type-safe, productive, useful, ...  That sounds like a feature for
Ada ;-).  Note that Smalltalk, Object Pascal, Modula-3, Oberon-X,
and Objective-C all have chosen to stick with single inheritance.
There are (ivory tower ;-) researchers studying ways to add MI in 
some of these languages, but the practitioners seem to be plowing
ahead very productively and happily relying on the rock-solid
single inheritance, building up their conceptual multiple inheritance
hierarchies as they see fit, in an application-specific way.

We expect that most Ada 95 programmers will find the same thing.
Single inheritance is there, in its full glory and simplicity,
right in the language.  In addition, there are building blocks
for constructing robust application-specific solutions to problems
that might otherwise use a language-provided multiple inheritance
mechanism.

Furthermore, what happens in some cases, is that the language-provided
multiple inheritance mechanism turns out to be not quite the right
solution for a given problem that is "reminiscent" of multiple inheritance.
Then you have the worst of both worlds -- a language made more complex
by the addition of linguistic multiple inheritance, and an application made 
more difficult by the lack of appropriate building blocks for solving the
problem the "right" way.

To summarize, it is our view that it is a bit ivory-towerish to
look at a language, and say that if it doesn't have feature X implemented
in some specific way, then it is useless and FUBAR.  I can't tell
you how many times we have been threatened over the past 5 years with
similar statements about all kinds of obscure features.  It is interesting
to follow the C++ standardization process, where the same desparate
statements are made in support of one obscure feature after another.

The fact is that a good language has its own self-consistent logic,
and some features make a good addition to that structure, and others
just don't fit.  We couldn't find a language-provided multiple inheritance
mechanism that enhanced Ada.  We could find plenty that made it more
complicated, less intuitive, less reliable, less readable, etc.
We welcomed proposals and suggestions for one that would be a net
addition to the language, but on deeper investigation into the nitty-gritty, 
we never found one where the pluses outweighed the minuses.  We had 
an "ivory-tower" desire to try to find one, but we had a more practical 
concern that we didn't want to FU the language to get one in, just for 
the marketing potential.  We wanted the features to be useful and 
reliable, not just there for show.  Perhaps the designers of 
Ada 007 will find the silver bullet...  Meanwhile, we will be productively
building reliable systems in our cement basement.

>John Goodsen                         Currently on-site at:
>The Dalmatian Group                       JP Morgan 
>User Interface Specialists                60 Wall St., New York City
>jgoodsen@radsoft.com                      jgoodsen@jpmorgan.com

S. Tucker Taft   stt@inmet.com
Ada 9X Mapping/Revision Team
Intermetrics, Inc.
Cambridge, MA  02138



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

* Re: Ada 90 inheritance request?
  1994-12-15 18:41         ` Robert I. Eachus
@ 1994-12-19 18:58           ` John Goodsen
  1994-12-20 10:40             ` Robert I. Eachus
                               ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: John Goodsen @ 1994-12-19 18:58 UTC (permalink / raw)


In article <EACHUS.94Dec15184124@spectre.mitre.org> eachus@spectre.mitre.org (Robert I. Eachus) writes:

      Never being afraid to jump into the fray, I'll respond to John by
   taking his MI example and asking why the obvious Ada 95 version is not
   acceptable: 

       type Needs_Finalization is new Controlled with...;
       -- overriding of Initialize/Adjust/Finalize
       procedure Initialize (Obj : in out Needs_Finalization);
       procedure Adjust     (Obj : in out Needs_Finalization);
       procedure Finalize   (Obj : in out Needs_Finalization);

       type Ctrl_T is new T with record
	 NF:  Needs_Finalization;
       end record;


Maybe it's just me not seeing something here (which could be an indicator
of the usability issues involved with addressing MI in Ada95), but where 
is the polymorphic behavior in the MI lattice?  Wouldn't the above solution
require me to delegate methods to the NF member of Ctrl_T (by hand)?

       Now I can think of cases where this won't work, but they are
   precisely the cases where the "true" MI version won't work either.

   No, I take that back.  There are cases where true MI will fail, and
   the Ada 95 version will sail through.  For example, if someone
   modifies T so that it inherits directly from Controlled...

       In general where Ada 95 says "this is unsafe, so we will not allow
   it," most MI languages say "this is really neat, and if you are VERY
   careful, it will work, but never on large projects, because it doesn't
   scale well."

	I've been there, I've done that, I've seen the maintenance
   nightmares that can occur when more than one programmer depends on
   unsafe MI on the same project.

This isn't clear to me.  Can you give an example (besides the overused
diamond inheritance pattern) ?  What do you mean by "unsafe MI" ?

thanks
-- 
--
John Goodsen                         Currently on-site at:
The Dalmatian Group                       JP Morgan 
User Interface Specialists                60 Wall St., New York City
jgoodsen@radsoft.com                      jgoodsen@jpmorgan.com



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

* Re: Ada 90 inheritance request?
  1994-12-19 18:58           ` John Goodsen
@ 1994-12-20 10:40             ` Robert I. Eachus
  1994-12-21 16:02             ` Norman H. Cohen
  1994-12-22  1:21             ` Bob Duff
  2 siblings, 0 replies; 13+ messages in thread
From: Robert I. Eachus @ 1994-12-20 10:40 UTC (permalink / raw)


In article <JGOODSEN.94Dec19135859@treasure.radsoft.com> jgoodsen@treasure.radsoft.com (John Goodsen) writes:

  > Maybe it's just me not seeing something here (which could be an indicator
  > of the usability issues involved with addressing MI in Ada95), but where 
  > is the polymorphic behavior in the MI lattice?  Wouldn't the above solution
  > require me to delegate methods to the NF member of Ctrl_T (by hand)?

  You lost me here. Initialize, Finalize, and Adjust have to be
defined on NF, but not on Ctrl_T.  All the operations on T
are derived for Ctrl_T, and appropriate dispatching occurs.  The ONLY
issue, as has been discussed is if you need finalization, etc. for
some part of the inherited T, or if you need access to the inherited
parts of T to do the initialization, etc.  But as I (and others) see
it, the major problem in that case is the broken abstraction, not the
unsafe approaches to implementing the operations.

 > This isn't clear to me.  Can you give an example (besides the overused
 > diamond inheritance pattern) ?  What do you mean by "unsafe MI" ?

    The easiest definition on a large project is any case where a new
subclass of one of the parents (or a change to one of the existing
subclasses) can break the MI inheriting class.  The usual break is
when a new subclass of class A has a method which conflicts with a
method inherited from class B, and you apply a class-wide operation of
the MI class to a member of that subclass (with casting as necessary).
You can get a dispatching call in some other method which now calls
the "wrong" method, and there is no point visible to a single
programmer where all this is visible.

    That's the major "doesn't scale" problem.  Ada 9X avoids it by
insisting that both of the methods involved must have a common
template inherited from a single parent.  If you have two mix-ins with
the "same" method, the visibility rules, not the dispatching rules
determine which gets called.  (Unless, as stated, they both explicitly
match a template in a superclass.  But in that case, we do have a
warning flag.)
  

--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...



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

* Re: Ada 90 inheritance request?
  1994-12-19 18:58           ` John Goodsen
  1994-12-20 10:40             ` Robert I. Eachus
@ 1994-12-21 16:02             ` Norman H. Cohen
  1994-12-22  1:21             ` Bob Duff
  2 siblings, 0 replies; 13+ messages in thread
From: Norman H. Cohen @ 1994-12-21 16:02 UTC (permalink / raw)


In article <JGOODSEN.94Dec19135859@treasure.radsoft.com>,
jgoodsen@treasure.radsoft.com (John Goodsen) writes: 

|> In article <EACHUS.94Dec15184124@spectre.mitre.org> eachus@spectre.mitre.org (Robert I. Eachus) writes: 
|>
|>       Never being afraid to jump into the fray, I'll respond to John by
|>    taking his MI example and asking why the obvious Ada 95 version is not
|>    acceptable: 
|>
|>        type Needs_Finalization is new Controlled with...;
|>        -- overriding of Initialize/Adjust/Finalize
|>        procedure Initialize (Obj : in out Needs_Finalization);
|>        procedure Adjust     (Obj : in out Needs_Finalization);
|>        procedure Finalize   (Obj : in out Needs_Finalization);
|>
|>        type Ctrl_T is new T with record
|>       NF:  Needs_Finalization;
|>        end record;
|>
|>
|> Maybe it's just me not seeing something here (which could be an indicator
|> of the usability issues involved with addressing MI in Ada95), but where
|> is the polymorphic behavior in the MI lattice?  Wouldn't the above solution
|> require me to delegate methods to the NF member of Ctrl_T (by hand)?

Here's what I think you're not seeing:  EVERY Ada object, whether or not
it is a controlled type, gets "finalized" when it is about to cease to
exist.  Finalization consists of calling that object's Finalize procedure
if the type is controlled, and then finalizing each of the object's
components, whether or not the type is controlled.  (See RM95 section
7.6.1, paragraphs 6 through 9.)  Thus when a Ctrl_T object is about to
cease to exist, the Finalize procedure for type Needs_Finalization is
called for the NF component of that object.

(C++ works the same way.  The difference comes in the treatment of any
finalization code for ancestors.  In C++, the base class destructor is
unavoidably invoked after the destructor for the derived class; in Ada,
it is up to the programmer whether a Finalize procedure for a derived
controlled type should invoke its parent's Finalize procedure.)

Perhaps you forgot the context of the question that was posed.  No
requirement was stated for Ctrl_T to publicly inherit any "normal"
operations of Needs_Finalization.  Rather, Needs_Finalization was
presumed to be part of the IMPLEMENTATION of the extension.  There was a
misconceived requirement for Ctrl_T to inherit the Finalization operation
of Needs_Finalization, but Robert Eachus's construction shows that this
is unnecessary.

|>                          Can you give an example (besides the overused
|> diamond inheritance pattern) ?  What do you mean by "unsafe MI" ?

If the diamond inheritance pattern is "overused", that's because the
problems it presents are compelling, all by themselves.

But there is a more general safety issue.  In a language with MI, given
two arbitrary classes A and B, it is always possible to construct an
object that can be used as both an A and a B, so one cannot recognize
uses of an A as a B as illegitimate at compile time.  (The legitimate
uses, but they are so few and far between that it is not worth opening
gaping holes in the type system.)

This issue shows up in the new C++ dynamic_cast feature.  In Ada,
conversion from access-to-S'Class to access-to-T'Class is not allowed
unless S is derived from T or T is derived from S.  (If T is derived from
S, a run-time check is performed to ensure that the access-to-S'Class
value actually points to an object whose type is derived from T; if S is
derived from T, no run-time check is necessary.) In any other case, the
conversion is rejected as illegal at compile time.  In C++, a dynamic
cast from S* to T* is always legal at compile time.  It cannot be
rejected, because someone may later derive a class D from BOTH S and T,
in which case an S* pointing to a D can be safely converted to a T*.  (At
run time, the dynamic cast returns a null pointer if the S* value does
not point to an object of a class derived from T. :-( )

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



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

* Re: Ada 90 inheritance request?
  1994-12-19 18:58           ` John Goodsen
  1994-12-20 10:40             ` Robert I. Eachus
  1994-12-21 16:02             ` Norman H. Cohen
@ 1994-12-22  1:21             ` Bob Duff
  2 siblings, 0 replies; 13+ messages in thread
From: Bob Duff @ 1994-12-22  1:21 UTC (permalink / raw)


In article <JGOODSEN.94Dec19135859@treasure.radsoft.com>,
John Goodsen <jgoodsen@treasure.radsoft.com> wrote:
>In article <EACHUS.94Dec15184124@spectre.mitre.org> eachus@spectre.mitre.org (Robert I. Eachus) writes:
>       type Needs_Finalization is new Controlled with...;
>       -- overriding of Initialize/Adjust/Finalize
>       procedure Initialize (Obj : in out Needs_Finalization);
>       procedure Adjust     (Obj : in out Needs_Finalization);
>       procedure Finalize   (Obj : in out Needs_Finalization);
>
>       type Ctrl_T is new T with record
>	 NF:  Needs_Finalization;
>       end record;
>
>Maybe it's just me not seeing something here (which could be an indicator
>of the usability issues involved with addressing MI in Ada95), but where 
>is the polymorphic behavior in the MI lattice?  Wouldn't the above solution
>require me to delegate methods to the NF member of Ctrl_T (by hand)?

No.  When an object is finalized, all of its components are finalized
automatically.  If this weren't true, then finalization would be rather
badly broken -- any object that was a component of another object
wouldn't work right.

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



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

end of thread, other threads:[~1994-12-22  1:21 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1994-11-23 21:33 Ada 90 inheritance request? S M Ryan
1994-12-02 16:46 ` Tucker Taft
1994-12-09 17:26   ` Cyrille Comar
1994-12-11 18:47     ` Bob Duff
1994-12-12  3:15     ` Tucker Taft
1994-12-13 19:02     ` John Goodsen
1994-12-14 19:49       ` John Goodsen
1994-12-15 18:41         ` Robert I. Eachus
1994-12-19 18:58           ` John Goodsen
1994-12-20 10:40             ` Robert I. Eachus
1994-12-21 16:02             ` Norman H. Cohen
1994-12-22  1:21             ` Bob Duff
1994-12-17 13:55       ` Tucker Taft

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