home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / ada / 2589 < prev    next >
Encoding:
Internet Message Format  |  1992-09-11  |  12.7 KB

  1. Path: sparky!uunet!europa.asd.contel.com!darwin.sura.net!spool.mu.edu!caen!uakari.primate.wisc.edu!usenet.coe.montana.edu!rpi!bu.edu!inmet!spock!stt
  2. From: stt@spock.camb.inmet.com (Tucker Taft)
  3. Newsgroups: comp.lang.ada
  4. Subject: Multiple Inheritance and Ada 9X
  5. Summary: How to build a multiple inheritance type structure
  6. Message-ID: <1992Sep11.182034.26753@inmet.camb.inmet.com>
  7. Date: 11 Sep 92 18:20:34 GMT
  8. Sender: news@inmet.camb.inmet.com
  9. Distribution: comp.lang.ada
  10. Organization: Intermetrics Inc, Cambridge MA
  11. Lines: 326
  12. Nntp-Posting-Host: spock
  13.  
  14. Here is a Language Study Note the Ada 9X Mapping/Revision Team
  15. recently distributed to the reviewers of the Ada 9X revision.
  16. It partially addresses the use of multiple inheritance in
  17. Ada 9X.  Hopefully it will be somewhat enlightening...
  18.  
  19. -S. Tucker Taft   stt@inmet.com
  20. Ada 9X Mapping/Revision Team
  21. Intermetrics, Inc.
  22. Cambridge, MA  02138
  23. -----------------------------------
  24. !topic LSN on Multiple Inheritance in Ada 9X
  25. !key LSN-1033 on Multiple Inheritance in Ada 9X
  26. !reference MS-3.4.1;4.6
  27. !reference MS-3.6.1;4.6
  28. !reference MS-12.3.6;4.6
  29. !from Tucker Taft $Date: 92/09/02 17:44:17 $ $Revision: 1.1 $
  30. !discussion
  31.  
  32. This Language Study Note discusses the creation of
  33. multiple inheritance type (semi-)lattices using the proposed
  34. Ada 9X object-oriented programming features.  
  35. It is in part directed at programmers familiar with
  36. other object-oriented programming languages that 
  37. build in syntax for a particular multiple-inheritance mechanism,
  38. rather than simply providing features needed to support it.
  39.  
  40. In this discussion, we will in general use Ada 9X terminology, where
  41. every object has a single "type," and multiple similar types
  42. (typically in some kind of hierarchy or oligarchy)
  43. form a "class" of types.  If we want to use the term "class" as
  44. it is used in C++ or Eiffel, we will always say "C++ class" or
  45. "Eiffel class."
  46.  
  47. In some languages, such as Eiffel, multiple inheritance
  48. serves many purposes.  For example, there is no equivalent in Eiffel
  49. to the "include" statement of C/C++ or the "with/use" clauses
  50. of Ada.  Therefore, to gain direct visibility to the
  51. declarations of some other module, one must inherit from
  52. that module (Eiffel class).
  53.  
  54. In C/C++, one can simply "include" a file containing
  55. a set of type and object definitions.
  56.  
  57. In Ada, one first identifies the external modules of interest
  58. via "with" clauses, and then chooses selectively whether
  59. to make only the name of the module (package) visible, or
  60. its contents (via a "use" clause).
  61.  
  62. In both Eiffel and C++, one can choose to inherit from
  63. some other type, without making that visible to clients
  64. of the new type.  Effectively, the linguistic multiple 
  65. inheritance mechanism is being used to express not
  66. an "is a refinement of" relationship, but rather 
  67. an "is implemented using" relationship.
  68.  
  69. Finally, there are the situations where
  70. a single type visibly inherits from two or
  71. more other types.  In these cases, this is rarely
  72. a symmetric situation.  Rather, one of the ancestor
  73. types is the "primary" ancestor, while the others
  74. are typically "mix-ins" designed to augment behavior
  75. of the primary ancestor.
  76.  
  77. Ada 9X supports multiple-inheritance module inclusion
  78. (via multiple "with"/"use" clauses), multiple-inheritance
  79. "is-implemented-using" via private extensions and record composition,
  80. and multiple-inheritance mix-ins via the use of
  81. generics, formal packages, and access discriminants.
  82.  
  83. The Ada 9X mechanisms are designed to eliminate "distributed" overhead,
  84. so that there is no added expense for the general user
  85. of the language because of the presence of the mechanisms supporting
  86. multiple inheritance.
  87.  
  88. There are basically three distinct situations associated
  89. with multi-inheritance mixins:
  90.  
  91.   1) The case where the mix-in provides components
  92.      and operations, and any overriding of these operations
  93.      needs only to look at the components of the mix-in itself.
  94.  
  95.   2) The case where the mix-in provides components and operations,
  96.      and some of the overriding of these operations needs access
  97.      to the whole object, rather than just the components of the mix-in.
  98.  
  99.   3) Like (2), and in addition, any object with the mix-in must
  100.      be able to be linked onto a list (or into some similar 
  101.      heterogeneous data structure) of other objects with the same mix-in.
  102.  
  103. Case (1) is handled completely in Ada 9X by a record or private extension,
  104. with the type being mixed in (in a possibly extended form) as a 
  105. component of the record extension.
  106.  
  107. Case (2) is handled with a generic, that takes any type in
  108. a given class (formal derived type), adds components (via extension) 
  109. and operations, and then reexports the extended type.  The new operations
  110. have access to the whole object, not just to the components
  111. being added.
  112.  
  113. Case (3) is handled with an access discriminant, that provides
  114. access to the enclosing object for the operations of the mix-in,
  115. while still allowing links through the mix-in.  Generics can
  116. also be used to simplify the definition.
  117.  
  118. Here are a few examples:
  119.  
  120. Case (1) --
  121.    One has an abstract type "Set_of_Strings" and one wants to implement
  122.    it by reusing an existing (concrete) type "Hash_Table":
  123.  
  124.    Here is the abstract type:
  125.  
  126.       type Set_Of_Strings is tagged limited private;
  127.       type Element_Index is new Natural;  -- Index within set.
  128.  
  129.       No_Element : constant Element_Index := 0;
  130.  
  131.       Invalid_Index : exception;
  132.  
  133.       procedure Enter(
  134.         -- Enter an element into the set, return the index
  135.          Set : in out Set_Of_Strings; 
  136.          S : String; 
  137.          Index : out Element_Index) is <>;
  138.  
  139.       procedure Remove(
  140.         -- Remove an element from the set; ignore if not there
  141.          Set : in out Set_Of_Strings; 
  142.          S : String) is <>; 
  143.  
  144.       procedure Combine(
  145.         -- Combine Additional_Set into Union_Set
  146.          Union_Set : in out Set_Of_Strings;
  147.          Additional_Set : Set_Of_Strings) is <>;
  148.  
  149.       procedure Intersect(
  150.         -- Remove all elements of Removal_Set from Intersection_Set
  151.          Intersection_Set : in out Set_Of_Strings;
  152.          Removal_Set : Set_Of_Strings) is <>;
  153.  
  154.       function Size(Set : Set_Of_Strings) return Element_Index is <>;
  155.         -- Return a count of the number of elements in the set
  156.  
  157.       function Index(
  158.         -- Return the index of a given element; return No_Element if not there.
  159.          Set : Set_Of_Strings; 
  160.          S : String) return Element_Index is <>;
  161.  
  162.       function Element(Index : Element_Index) return String is <>;
  163.         -- Return element at given index position
  164.         -- raise Invalid_Index if no element there.
  165.     private
  166.       type Set_Of_Strings is tagged limited ...
  167.  
  168.    Here is an implementation of this abstract type that
  169.    inherits its interface from Set_Of_Strings, and its implementation
  170.    from Hash_Table:
  171.  
  172.       type Hashed_Set(Table_Size : Positive) is 
  173.         new Set_Of_Strings with private;
  174.  
  175.       -- Now we give the specs of the operations being implemented
  176.       procedure Enter(
  177.         -- Enter an element into the set, return the index
  178.          Set : in out Hashed_Set;
  179.          S : String; 
  180.          Index : out Element_Index);
  181.  
  182.       procedure Remove(
  183.         -- Remove an element from the set; ignore if not there
  184.          Set : in out Hashed_Set;
  185.          S : String);
  186.       . . . etc.
  187.  
  188.     private
  189.       type Hashed_Set(Table_Size : Positive) is 
  190.         new Set_Of_Strings with record
  191.           Table : Hash_Table(1..Table_Size);
  192.         end record;
  193.  
  194.    In the body of this package, we would provide the bodies
  195.    for each of the operations, in terms of the operations
  196.    available from Hash_Table.  Chances are they don't match exactly,
  197.    so a little bit of "glue" code will be necessary in any case.
  198.       
  199.   
  200. Case (2) --
  201.    One has a type Basic_Window that responds to various
  202.    events and calls.  One wants to embellish the Basic_Window
  203.    in various ways with various mix-ins.
  204.  
  205.      type Basic_Window is tagged limited private;
  206.      procedure Display(W : Basic_Window);
  207.      procedure Mouse_Click(W : in out Basic_Window; Where : Mouse_Coords);
  208.      . . . 
  209.  
  210.    Now one can define any number of mix-in generics, like the following:
  211.   
  212.      generic
  213.        type Some_Window is new Window with private;
  214.          -- take in any descendant of Window
  215.      package Label_Mixin is
  216.        type Window_With_Label is new Some_Window with private;
  217.          -- Jazz it up somehow.
  218.  
  219.        -- Overridden operations:
  220.        procedure Display(W : Window_With_Label);
  221.  
  222.        -- New operations:
  223.        procedure Set_Label(W : in out Window_With_Label; S : String);
  224.          -- Set the label
  225.        function Label(W : Window_With_Label) return String;
  226.          -- Fetch the label
  227.      private
  228.        type Window_With_Label is 
  229.          new Some_Window with record
  230.            Label : String_Quark := Null_Quark;  
  231.              -- An XWindows-Like unique ID for a string
  232.          end record;
  233.  
  234.    In the generic body, we implement the Overridden and New operations
  235.    as appropriate, using any inherited operations, if necessary.  For
  236.    example, this might be an implementation of the overridden Display:
  237.  
  238.        procedure Display(W : Window_With_Label) is
  239.        begin
  240.            Display(Some_Window(W));
  241.              -- First display the window normally,
  242.              -- by passing the buck to the parent type.
  243.  
  244.            if W.Label /= Null_Quark then
  245.              -- Now display the label if it is not null
  246.                Display_On_Screen(XCoord(W), YCoord(W)-5, Value(W.Label));
  247.                  -- Use two inherited functions on Basic_Window
  248.                  -- to get the coordinates where to display the label.
  249.            end if;
  250.        end Display;
  251.  
  252.    Presuming we have several such generic packages defined,
  253.    We can now create the desired window by mixing in repeatedly.
  254.    First we declare the tailored window as a private extension
  255.    of Basic_Window, and then we define it via a sequence of
  256.    instantiations:
  257.  
  258.        type My_Window is Basic_Window with private;
  259.        . . .
  260.      private
  261.        package Add_Label is new Label_Mixin(Basic_Window);
  262.        package Add_Border is 
  263.          new Border_Mixin(Add_Label.Window_With_Label);
  264.        package Add_Menu_Bar is 
  265.          new Menu_Bar_Mixin(Add_Border.Window_With_Border);
  266.  
  267.        type My_Window is 
  268.          new Add_Menu_Bar.Window_With_Menu_Bar with null;
  269.            -- Final window is a null extension of Window_With_Menu_Bar.
  270.            -- We could instead make a record extension and
  271.            -- add components for My_Window over and above those
  272.            -- needed by the mix-ins.
  273.  
  274. Case(3) --
  275.    In this case, let us presume we have two independent
  276.    hierarchies, one for Windows, which represent areas on the
  277.    screen, and one for Monitors, which wait for an object to change,
  278.    and then do something in reaction to it.
  279.    An object that supports Monitors keeps a linked list of
  280.    Monitors, and calls their Update operation whenever the object
  281.    is changed.  For example:
  282.  
  283.      type Monitor;
  284.      type Monitor_Ptr is access Monitor'CLASS;
  285.  
  286.      type Monitored_Object is tagged limited 
  287.        -- Monitored objects are derived from this root type
  288.        record
  289.          First : Monitor_Ptr;  - List of monitors
  290.          -- More components to be added by extension
  291.        end record;
  292.  
  293.      type Monitored_Object_Ptr is access Monitored_Object'CLASS;
  294.   
  295.      type Monitor is tagged limited 
  296.        record
  297.          Next : Monitor_Ptr;
  298.          Obj : Monitored_Object_Ptr;
  299.          -- More components to be added by extension
  300.        end record;
  301.      procedure Update(M : in out Monitor) is <>;
  302.        -- Dispatching operation, called when monitored object
  303.        -- is changed.
  304.  
  305.    Now suppose we want to create a Window that can
  306.    act as a Monitor as well as a Window:
  307.  
  308.    First we define a mix-in that is a monitor, and override its Update op:
  309.  
  310.      type Monitor_Mixin(Win : access Basic_Window'CLASS) is 
  311.        new Monitor with null;
  312.      procedure Update(M : in out Monitor_Mixin);
  313.  
  314.    The body for this Update could be:
  315.  
  316.      procedure Update(M : in out Monitor_Mixin) is
  317.        -- On an Update, simply re-Display the window
  318.      begin
  319.          Display(M.Win);  -- This is a dispatching call
  320.      end Update;
  321.  
  322.    Now we can "mix" this Monitor_Mixin into any window type, as follows:
  323.  
  324.      type Window_That_Monitors is 
  325.        new My_Window with record
  326.          Mon : Monitor_Mixin(Window_That_Monitors'ACCESS);
  327.        end record;
  328.  
  329.    We could define a tailored Monitor mix-in that did something 
  330.    besides just call the Display operation of the associated
  331.    Window.  But in many cases, this simple one will do the job.
  332.  
  333. -----------------------------
  334. As these examples illustrate, Ada 9X provides support
  335. for the construction of essentially arbitrary multiple
  336. inheritance type lattices, without having to commit itself
  337. to a single linguistic mechanism for multiple inheritance, and
  338. without burdening simple single-inheritance problems with the complexity
  339. and implementation burden of linguistic multiple inheritance.
  340.