comp.lang.ada
 help / color / mirror / Atom feed
* Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
@ 1994-09-27 16:52 John Volan
  1994-09-27 18:48 ` Mark A Biggar
  1994-09-28 14:01 ` Norman H. Cohen
  0 siblings, 2 replies; 43+ messages in thread
From: John Volan @ 1994-09-27 16:52 UTC (permalink / raw)


As a long-time Ada evangelist and self-styled acolyte of Team-Ada (ahh gimme
that ol' time language religion! :-), I'm quite pleased with the new OO 
features of Ada 9X.  One of the things I like is that Ada 9X does *NOT* follow
the crowd of other OO languages in one very important particular:  *NO* attempt
was made to merge the features of packages and types into some kind of new
"class" construct.   Issues of modularity, encapsulation, information hiding,
interface control, scoping, inter-module dependency, namespace control, generic 
templating, etc., are still the province of packages.  Issues of data 
abstraction, strong typing, type derivation and inheritance, etc., are still 
the province of types.   And the place where these two areas intersect is 
still the same: private types.

IMHAARO (In My Humble And Admittedly Religious Opinion :-) this kind of 
separation of concerns is extremely beneficial.  I've looked at other
OO languages that use the "class" as both the unit of data abstraction and
the unit of modularity (mostly Smalltalk, C++, Eiffel), and my gut reaction
is that this attempt to merge features into a single construct leads to a lot
of unnecessary "baroqueness," in both syntax and semantics.  C++'s constructors
and destructors, and Eiffel's "anchor" and "like" mechanisms, come to mind as 
prime examples of this.  (Please, no fire and brimstone from you folks in the 
other religions -- it's MHAARO after all, and I'd certainly welcome hearing 
yours... :-)

However, there is one specific kind of situation that I'm having difficulty
mapping into Ada 9X:  How can you achieve mutual recursion between two classes
of object without breaking encapsulation?  In other words, you have two classes
of objects that must "know" about each other's interfaces, but you don't want
to have to expose their implementations to each other in order to do that.

Consider a simple example where we have two classes related in a simple
one-to-one association:

	Employee -- occupies -- Office

Of course, associations are conceptually bi-directional, so this could be
expressed equivalently as:

	Office -- is occupied by -- Employee

If our application needs to traverse this association only in one direction or 
the other, then we are free to choose a design where only one of the classes
has to "know" about the other class.  It's fairly straightforward to map such
a design into an Ada coding style that encapsulates each class as a private
type declared in its own package.   One class "knowing" about the other 
translates into one package importing the other via a "with" clause.  (We can 
sort of mimic this kind of Ada modularization in C++ by putting each C++ class 
in a separate header file and having one header file "#include" the other.)

For instance, suppose our application needs to be able to start from a given 
Employee object and look up its occupied Office object.  We could have:

    ----------------------------------------------------------------------

    with ...; -- other stuff, but no knowledge about the Employee package

    package Office is

      type Object is tagged limited private;  -- perhaps we'll distinguish
                                              -- different subclasses of Office
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- various subprograms involving that "other stuff"

    private
      type Object is tagged
        record
          ... -- various components involving that "other stuff"
        end record;
    end Office;

    ----------------------------------------------------------------------

    with ...; -- other stuff
    with Office;  -- Employee class "knows" about Office class

    package Employee is

      type Object is tagged limited private; -- perhaps we'll distinguish
                                             -- different subclasses of Employee
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- various subprograms involving that "other stuff"

      function Office_Occupied_By (The_Employee : in Employee.Object) 
        return Office.Pointer;

      procedure Occupy (The_Employee : in out Employee.Object;
                        The_Office   : in     Office.Pointer);
      ...

    private
      type Object is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupied_Office : Office.Pointer;
          ...
        end record;
    end Employee;

    ----------------------------------------------------------------------

The equivalent in C++ might be:

    //////////////////////////////////////////////////////////////////////

    // File: Office.h

    #ifndef _Office_h
    #define _Office_h

    #include " ... other stuff ... "

    class Office {
    public:
      ...  // various member functions involving that "other stuff"
    private:
      ...  // various data members involving that "other stuff"
    };

    #endif _Office_h

    //////////////////////////////////////////////////////////////////////

    // File: Employee.h

    #ifndef _Employee_h
    #define _Employee_h

    #include " ... other stuff ... "
    #include "Office.h"

    class Employee {
    public:
      ...  // various member functions involving that "other stuff"
      Office* occupiedOffice () const;
      void occupy (Office* theOffice);
      ...
    private:
      ...  // various data members involving that "other stuff"
      Office* itsOccupiedOffice;
      ..
    };
    
    #endif _Employee_h

    //////////////////////////////////////////////////////////////////////

Conversely, if our application needs to be able to start from a given Office
object and look up the Employee object which occupies it, then we just need to
flip the dependency around:

    ----------------------------------------------------------------------

    with ...; -- other stuff, but no knowledge about the Office package

    package Employee is

      type Object is tagged limited private; -- perhaps we'll distinguish
                                             -- different subclasses of Employee
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- various subprograms involving that "other stuff"

    private
      type Object is tagged
        record
          ... -- various components involving that "other stuff"
        end record;
    end Employee;

    ----------------------------------------------------------------------

    with ...; -- other stuff
    with Employee;  -- Office class "knows" about Employee class

    package Office is

      type Object is tagged limited private;  -- perhaps we'll distinguish
                                              -- different subclasses of Office
      type Object is tagged limited private;
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- various subprograms involving that "other stuff"

      function Employee_Occupying (The_Office : in Office.Object) 
        return Employee.Pointer;

      procedure Occupy (The_Office   : in out Office.Object;
                        The_Employee : in     Employee.Pointer);
      ...

    private
      type Object is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupying_Employee : Employee.Pointer;
          ...
        end record;
    end Office;

    ----------------------------------------------------------------------

The equivalent in C++ might be:

    //////////////////////////////////////////////////////////////////////

    // File: Employee.h

    #ifndef _Employee_h
    #define _Employee_h
    #include " ... other stuff ... "

    class Employee {
    public:
      ...  // various member functions involving that "other stuff"
    private:
      ...  // various data members involving that "other stuff"
    };

    #endif _Employee_h

    //////////////////////////////////////////////////////////////////////

    // File: Office.h

    #ifndef _Office_h
    #define _Office_h
    #include " ... other stuff ... "
    #include "Employee.h"

    class Office {
    public:
      ...  // various member functions involving that "other stuff"
      Employee* occupyingEmployee () const;
      void beOccupiedBy (Employee* theEmployee);
      ...
    private:
      ...  // various data members involving that "other stuff"
      Employee* itsOccupyingEmployee;
      ..
    };
    
    #endif _Office_h

    //////////////////////////////////////////////////////////////////////

In either of these scenarios, we have been able to maintain complete 
encapsulation of both object classes.  The class in the second package is 
always required to deal with the class from the first package strictly through
the interface that appears in the public part of the first package.

But what if we need to be able to traverse the association in *both*
directions?  The most straightforward design to meet this requirement would
make the data types for Employee and Office mutually recursive: Each class
of object would contain a reference [1] to an object of the other class.  This
is easy enough to do (in either Ada or C++) by "forward declaring" the types
so that both of them can "know" about each other -- but it appears that we'd 
have to relinquish the idea of putting the two classes in separate packages.

Here's my attempt at mutually recursive classes in Ada 9X:

    ----------------------------------------------------------------------

    with ... ; -- other stuff needed by Employee
    with ... ; -- other stuff needed by Office

    package Employee_And_Office is

      type Employee is tagged limited private; -- already acts as a forward decl
      type Employee_Pointer is access all Employee'Class;
      No_Employee : constant Employee_Pointer := null;

      ... -- Employee subprograms involving that "other stuff"

      type Object is tagged limited private; -- already acts as a forward decl
      type Office_Pointer is access all Office'Class;
      No_Office : constant Office_Pointer := null;

      ... -- Office subprograms involving that "other stuff"

      function Office_Occupied_By (The_Employee : in Employee)
        return Office_Pointer;

      function Employee_Occupying (The_Office : in Office)
        return Employee_Pointer;

      procedure Occupy (The_Office   : in Office_Pointer;
                        The_Employee : in Employee_Pointer);
      -- Hmm ... does this count as a primitive/dispatching operation for
      -- *both* tagged types, and therefore a compiler error?
      
    private

      type Employee is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupied_Office : Office_Pointer;
          ...
        end record;
  
      type Office is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupying_Employee : Employee_Pointer;
          ...
        end record;
  
    end Employee_And_Office;

    ----------------------------------------------------------------------

Here's my equivalent C++:

    //////////////////////////////////////////////////////////////////////

    // File: EmployeeAndOffice.h

    #ifndef _EmployeeAndOffice_h
    #define _EmployeeAndOffice_h

    #include " ... other stuff needed by Employee ... "
    #include " ... other stuff needed by Office   ... "

    class Employee;  // forward declaration
    class Office;    // forward declaration

    class Employee {
    public:
      ...  // various member functions involving that "other stuff"
      Office* occupiedOffice () const;
      friend void occupy (Office* theOffice, Employee* theEmployee);
      ...
    private:
      ...  // various data members involving that "other stuff"
      Office* itsOccupiedOffice;
      ..
    };
    
    class Office {
    public:
      ...  // various member functions involving that "other stuff"
      Employee* occupyingEmployee () const;
      friend void occupy (Office* theOffice, Employee* theEmployee);
      ...
    private:
      ...  // various data members involving that "other stuff"
      Employee* itsOccupyingEmployee;
      ..
    };
    
    #endif _EmployeeAndOffice_h

    //////////////////////////////////////////////////////////////////////

In both the Ada 9X and C++ versions we do need to break encapsulation a bit:
The two classes had to be stuck in the same package, and the Occupy procedure, 
which establishes the association between an Employee and an Office, needs 
to have visibility to the implementations of both classes (in the C++ version, 
it's a "friend" of both classes).  However, in the C++ version, that's the 
*only* point where we need to break encapsulation.  If Employee and Office 
objects need to interface each other in any other way, they have to go through 
the public member functions.  In the Ada version, on the other hand, having
both types in the same package broke encapsulation between them once and for 
all.  Any Employee subprogram can exploit detailed knowledge of the 
implementation of the Office type, and vice versa.  (Of course, we still 
haven't broken encapsulation with respect to any *other* classes -- clients of 
Employee and Office are still restricted to using their public interfaces.)

So my question is: Is there some other arrangement of Ada 9X packages (perhaps
involving the new hierarchical library mechanism and child units) that allows
for this kind of mutual recursion, and yet preserves the same amount of
encapsulation that the C++ version does?

Thanks in advance for any help,

-- John Volan

FOOTNOTE:

[1]  In the above discussion, I used the term "reference to an object" as an
attempt to generalize the notion of a type of data that does not represent the
object itself, but which in some way "designates" or "refers to" or "expresses
the identity of" the object.  The most obvious implementation of this notion
is pointers (Ada access values), and this implementation is assumed in my
code examples above.  

However, this is not the only possibility.  Numeric indexes to a statically
allocated array of objects can act as "identification numbers" for those 
objects, and this leads to a possible solution of the problem I posed above: 
You could create packages that just introduce the identification-number types, 
then use them to introduce the object-type packages. Now, where those 
statically-allocated arrays of objects would have to go, that's another 
question.  This solution is obviously more complicated, and less generally
applicable than pointers.  So I'd still like to know of any better solution
that might exist.

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-27 16:52 Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) John Volan
@ 1994-09-27 18:48 ` Mark A Biggar
  1994-09-29  1:46   ` John Volan
  1994-09-28 14:01 ` Norman H. Cohen
  1 sibling, 1 reply; 43+ messages in thread
From: Mark A Biggar @ 1994-09-27 18:48 UTC (permalink / raw)


[Discussion of how to set up mutualy recursive tagged types]

First of all you really only need to have a forward definition of one
of the two types, lets choose office.

So first define a abstract office type that doesn't knoe about employees:

	package Abs_Office is

	type Abstract_Office is abstract tagged null record;
	type Access_Office is access all Abstract_Office'class;

	end Abs_Office;


Now we can define the employee type in terms of the above abstract type:

	with Abs_Office; use Abs_Office;
	package Emp is

	type Employee is tagged record
		Office: Access_Office;
	end record;
	type Employee_Access is access all Employee'class;

	end Emp;

Now we can define the real office type:

	with Abs_Office; use Abs_Office;
	with Emp; use Emp;
	package Off is

	type Office is new Abstract_Office with record
		Employee: Employee_Access;
	end record;

	end Off;

This of course ignores the private aspects of the C++ example given in the 
original article, but these are easly added using private types and/or
private extensions in the above packages.

Moral: if you need mutualy recursive tagged types with out breaking
encapsulation, then use an abstract type for you forward reference.

--
Mark Biggar
mab@wdl.loral.com










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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-27 16:52 Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) John Volan
  1994-09-27 18:48 ` Mark A Biggar
@ 1994-09-28 14:01 ` Norman H. Cohen
  1994-09-29  2:12   ` John Volan
  1994-09-29  9:48   ` Magnus Kempe
  1 sibling, 2 replies; 43+ messages in thread
From: Norman H. Cohen @ 1994-09-28 14:01 UTC (permalink / raw)


In article <1994Sep27.165203.9192@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com
(John Volan) writes: 

|> But what if we need to be able to traverse the association in *both*
|> directions?  The most straightforward design to meet this requirement would
|> make the data types for Employee and Office mutually recursive: Each class
|> of object would contain a reference [1] to an object of the other class.  This
|> is easy enough to do (in either Ada or C++) by "forward declaring" the types
|> so that both of them can "know" about each other -- but it appears that we'd
|> have to relinquish the idea of putting the two classes in separate packages.
|>
|> Here's my attempt at mutually recursive classes in Ada 9X: 
|>
|>     ----------------------------------------------------------------------
|>
|>     with ... ; -- other stuff needed by Employee
|>     with ... ; -- other stuff needed by Office
|>
|>     package Employee_And_Office is
|>
|>       type Employee is tagged limited private; -- already acts as a forward decl
|>       type Employee_Pointer is access all Employee'Class;
|>       No_Employee : constant Employee_Pointer := null;
|>
|>       ... -- Employee subprograms involving that "other stuff"
|>
|>       type Object is tagged limited private; -- already acts as a forward decl
|>       type Office_Pointer is access all Office'Class;
|>       No_Office : constant Office_Pointer := null;
|>
|>       ... -- Office subprograms involving that "other stuff"
|>
|>       function Office_Occupied_By (The_Employee : in Employee)
|>         return Office_Pointer;
|>
|>       function Employee_Occupying (The_Office : in Office)
|>         return Employee_Pointer;
|>
|>       procedure Occupy (The_Office   : in Office_Pointer;
|>                         The_Employee : in Employee_Pointer);
|>       -- Hmm ... does this count as a primitive/dispatching operation for
|>       -- *both* tagged types, and therefore a compiler error?

(Actually, it's not a dispatching operation for either, since you didn't
use access parameters.)

|>
|>     private
|>
|>       type Employee is tagged
|>         record
|>           ... -- various components involving that "other stuff"
|>           Its_Occupied_Office : Office_Pointer;
|>           ...
|>         end record;
|>
|>       type Office is tagged
|>         record
|>           ... -- various components involving that "other stuff"
|>           Its_Occupying_Employee : Employee_Pointer;
|>           ...
|>         end record;
|>
|>     end Employee_And_Office;

One variation on this is to declare recursive types meant to serve as the
parents of types Employee and Office, but to provide no operations for
these recursive types.  Then, in child packages, declare Employee and
Office themselves as derived types and declare primitive operations in
those child packages: 

    with ... ; -- other stuff needed by Employee
    with ... ; -- other stuff needed by Office

    package Employees_And_Offices is

      type Employee_Parent is tagged limited private;
      type Employee_Pointer is access all Employee'Class;

      type Office_Parent is tagged limited private;
      type Office_Pointer is access all Office'Class;

    private

      type Employee_Parent is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupied_Office : Office_Pointer;
          ...
        end record;

      type Office_Parent is tagged
        record
          ... -- various components involving that "other stuff"
          Its_Occupying_Employee : Employee_Pointer;
          ...
        end record;

    end Employees_And_Offices;

    package Employees_And_Offices.Employees is
       type Employee is tagged private;
       No_Employee : constant Employee_Pointer := null;
      ... -- Employee subprograms involving that "other stuff"
    private
       type Employee is new Employee_Parent with null record;
    end Employees_And_Offices.Employees;

    package Employees_And_Offices.Offices is
       type Office is tagged private;
       No_Office : constant Office_Pointer := null;
      ... -- Office subprograms involving that "other stuff"
    private
       type Office is new Office_Parent with null record;
    end Employees_And_Offices.Offices;


    with Employees_And_Offices.Employees, Employees_And_Offices.Offices;

    package Employees_And_Offices.Common is

      function Office_Occupied_By (The_Employee : in Employee)
        return Office_Pointer;

      function Employee_Occupying (The_Office : in Office)
        return Employee_Pointer;

      procedure Occupy (The_Office   : in Office_Pointer;
                        The_Employee : in Employee_Pointer);

    end Employees_And_Offices.Common;

The operations in the Common child are not primitive operations of either
type.  (Two types cannot possibly both have primitive operations with
parameters of the other type, because the declarations of the packages
declaring those types would have to name each other in with clauses.  If
you don't mind breaking the symmetry, you can make the Offices child
depend on the Employees child and put ALL the common operations in the
Offices child as primitive operations of type Office, or vice versa.)
The bodies of all three child packages have complete visibility into the
mutually recursive data structure declared in Employees_And_Offices.  Since
Office_Pointer is declared to designate Office_Parent'Class rather than
Office_Parent, there is no problem with Office_Pointer values designating
Office objects, and similarly for the Employee-related types.

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-27 18:48 ` Mark A Biggar
@ 1994-09-29  1:46   ` John Volan
  1994-09-29 13:57     ` Tucker Taft
                       ` (2 more replies)
  0 siblings, 3 replies; 43+ messages in thread
From: John Volan @ 1994-09-29  1:46 UTC (permalink / raw)


Mark,

Thanks for your suggestion, but I think there are some problems with it:

MAB = mab@dst17.wdl.loral.com (Mark A Biggar) writes:

MAB>[Discussion of how to set up mutualy recursive tagged types]
MAB>
MAB>First of all you really only need to have a forward definition of one
MAB>of the two types, lets choose office.

Okay, I can buy that.  I'd like to keep things symmetrical if possible, but
I can live without it if I have to ...

MAB>So first define a abstract office type that doesn't knoe about employees:
MAB>
MAB>	package Abs_Office is
MAB>
MAB>	type Abstract_Office is abstract tagged null record;
MAB>	type Access_Office is access all Abstract_Office'class;
MAB>
MAB>	end Abs_Office;

Hmmm... So, from this "abstract" point of view, an Office has no features
whatsoever.  No components or subprograms (whether publicly or privately
visible) that deal with the fact that an Office will point to an Employee,
nor even any of the "other stuff" that makes up an Office.  (Perhaps you're
leaving it open where that "other stuff" could go, but for now I'm going to
take what you've written above at face value).  Actually, I wouldn't call this
an "abstract" view of an Office.  Rather, let me define this as an abstract
*class* that provides an "opaque" *view* of an Office.

MAB>Now we can define the employee type in terms of the above abstract type:
MAB>
MAB>	with Abs_Office; use Abs_Office;
MAB>	package Emp is
MAB>
MAB>	type Employee is tagged record
MAB>		Office: Access_Office;
MAB>	end record;
MAB>	type Employee_Access is access all Employee'class;
MAB>
MAB>	end Emp;

... I'm going to assume that this Employee type would really have a lot of 
"other stuff" in it along with this pointer, and that this package spec would
also include all the primitive subprograms that define the Employee class, 
including subprograms that deal with the "other stuff" as well as the 
association with Office.  

The trouble here is that even though an Employee now has a pointer to an Office,
this pointer only gives the Employee an "opaque" view of an Office.  The 
"public" view of an Office is eventually going to be supplied via a *subclass*
of this "opaque" view -- but the Employee class doesn't have access to that
"public" view.

Although I didn't explicitly say so in my original post, I'm assuming that the 
Employee class might very well be a full client of the Office class (and vice 
versa). This means that the bodies of all Employee subprograms ought to have 
full visibility to the public subprograms of the Office class.  But even if
Employee isn't *itself* a client of Office, we certainly can assume that the
*clients* of Employee *will* be clients of Office as well!

Let's say some client of Employee asks an Employee object for its Office.  The
Employee object will give back one of these "opaque" pointers in response.  What
can the client do with that pointer?  As far as it can see, the Office object
being pointed to is totally featureless.  Oh, sure, it's possible that this
Office object is really some subclass of Abstract_Office that does have useful
features, but this information isn't available to the client.  The only way the
client could get to that information would be to use an unchecked conversion to 
change this "opaque" pointer into a pointer to the "non-opaque" Office subclass
below.  (I think folks in the other languages would call this "downcasting".)
But this practice breaks type-safety!

(Okay, maybe we could endow the abstract class with all the "other stuff", 
rather than wait until the subclass below.  That way, a client would at least
have visibility to that "other stuff".  But my feeling is that the Office's
association with the Employee is just as important a feature as the "other
stuff".  Maybe the client would want to ask the Office for its associated
Employee again later, for some reason.  Or maybe a client might want to tell
the Office to *change* its Employee--i.e., tell the old one to vamoose and let
some new guy occupy the Office! :-)

MAB>Now we can define the real office type:
MAB>
MAB>	with Abs_Office; use Abs_Office;
MAB>	with Emp; use Emp;
MAB>	package Off is
MAB>
MAB>	type Office is new Abstract_Office with record
MAB>		Employee: Employee_Access;
MAB>	end record;
MAB>
MAB>	end Off;

At least the full Office class gets visibility to the public interface of
the Employee class.  But not vice versa.

MAB>This of course ignores the private aspects of the C++ example given in the 
MAB>original article, but these are easly added using private types and/or
MAB>private extensions in the above packages.
MAB>
MAB>Moral: if you need mutualy recursive tagged types with out breaking
MAB>encapsulation, then use an abstract type for you forward reference.

Moral: An "opaque" abstract class might preserve encapsulation, but it doesn't
provide true mutual recursion.

MAB>
MAB>--
MAB>Mark Biggar
MAB>mab@wdl.loral.com

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------








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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-28 14:01 ` Norman H. Cohen
@ 1994-09-29  2:12   ` John Volan
  1994-09-29 14:01     ` Tucker Taft
  1994-09-29 18:37     ` Norman H. Cohen
  1994-09-29  9:48   ` Magnus Kempe
  1 sibling, 2 replies; 43+ messages in thread
From: John Volan @ 1994-09-29  2:12 UTC (permalink / raw)


NHC = ncohen@watson.ibm.com (Norman H. Cohen) writes:

NHC>One variation on this is to declare recursive types meant to serve as the
NHC>parents of types Employee and Office, but to provide no operations for
NHC>these recursive types.  Then, in child packages, declare Employee and
NHC>Office themselves as derived types and declare primitive operations in
NHC>those child packages: 
NHC>
NHC>    with ... ; -- other stuff needed by Employee
NHC>    with ... ; -- other stuff needed by Office
NHC>
NHC>    package Employees_And_Offices is
NHC>
NHC>      type Employee_Parent is tagged limited private;
NHC>      type Employee_Pointer is access all Employee'Class;
NHC>
NHC>      type Office_Parent is tagged limited private;
NHC>      type Office_Pointer is access all Office'Class;

This solution suffers from the same problem as Mark Biggar's suggestion:
These access types only give clients an "opaque" view of the designated objects.
The useful primitive operations for these objects won't be declared until we 
get to the concrete *subclasses* declared later.  So a client would have to
resort to using a non-typesafe Unchecked_Conversion to "downcast" one of these
pointers into designating the corresponding concrete subclass.

Another issue is whether the concrete subclasses declared below are going to
be the *only* subclasses of these abstract classes.  Certainly that's the
original intent -- but will a maintainer pay any attention? ;-)

NHC>    private
NHC>
NHC>      type Employee_Parent is tagged
NHC>        record
NHC>          ... -- various components involving that "other stuff"
NHC>          Its_Occupied_Office : Office_Pointer;
NHC>          ...
NHC>        end record;
NHC>
NHC>      type Office_Parent is tagged
NHC>        record
NHC>          ... -- various components involving that "other stuff"
NHC>          Its_Occupying_Employee : Employee_Pointer;
NHC>          ...
NHC>        end record;
NHC>
NHC>    end Employees_And_Offices;

Another problem here is that this doesn't really solve the original puzzle I
posed:  How do you avoid breaking encapsulation *between* these two classes?
These type declarations are private, but since the packages below are all
children of this parent package, they have complete visibility to the private
part of the parent.  So an Employee subprogram still has license to ignore
the public primitives of the Office class and "diddle" with the Office's
private components directly.  (And vice versa.)

NHC>    package Employees_And_Offices.Employees is
NHC>       type Employee is tagged private;
NHC>       No_Employee : constant Employee_Pointer := null;
NHC>      ... -- Employee subprograms involving that "other stuff"
NHC>    private
NHC>       type Employee is new Employee_Parent with null record;
NHC>    end Employees_And_Offices.Employees;
NHC>
NHC>    package Employees_And_Offices.Offices is
NHC>       type Office is tagged private;
NHC>       No_Office : constant Office_Pointer := null;
NHC>      ... -- Office subprograms involving that "other stuff"
NHC>    private
NHC>       type Office is new Office_Parent with null record;
NHC>    end Employees_And_Offices.Offices;
NHC>
NHC>
NHC>    with Employees_And_Offices.Employees, Employees_And_Offices.Offices;
NHC>
NHC>    package Employees_And_Offices.Common is
NHC>
NHC>      function Office_Occupied_By (The_Employee : in Employee)
NHC>        return Office_Pointer;
NHC>
NHC>      function Employee_Occupying (The_Office : in Office)
NHC>        return Employee_Pointer;
NHC>
NHC>      procedure Occupy (The_Office   : in Office_Pointer;
NHC>                        The_Employee : in Employee_Pointer);
NHC>
NHC>    end Employees_And_Offices.Common;
NHC>
NHC>The operations in the Common child are not primitive operations of either
NHC>type.  (Two types cannot possibly both have primitive operations with
NHC>parameters of the other type, because the declarations of the packages
NHC>declaring those types would have to name each other in with clauses.  If
NHC>you don't mind breaking the symmetry, you can make the Offices child
NHC>depend on the Employees child and put ALL the common operations in the
NHC>Offices child as primitive operations of type Office, or vice versa.)
NHC>The bodies of all three child packages have complete visibility into the
NHC>mutually recursive data structure declared in Employees_And_Offices.  Since
NHC>Office_Pointer is declared to designate Office_Parent'Class rather than
NHC>Office_Parent, there is no problem with Office_Pointer values designating
NHC>Office objects, and similarly for the Employee-related types.

Ah, but there *is* a problem: An Office_Pointer does't manifestly designate an
*Office* object.  Rather, it designates an *Office_Parent view* of what *might*
be an Office object.  This view, being "opaque", is not useful to a client.

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

Well, this doesn't really work, but I think we're starting to get closer ...
Thanks anyway!

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-28 14:01 ` Norman H. Cohen
  1994-09-29  2:12   ` John Volan
@ 1994-09-29  9:48   ` Magnus Kempe
  1994-09-29 13:10     ` Magnus Kempe
  1994-09-29 13:35     ` John Volan
  1 sibling, 2 replies; 43+ messages in thread
From: Magnus Kempe @ 1994-09-29  9:48 UTC (permalink / raw)



Norm's solution could be further improved as follows:

: One variation on this is to declare recursive types meant to serve as the
: parents of types Employee and Office, but to provide no operations for
: these recursive types.  Then, in child packages, declare Employee and
: Office themselves as derived types and declare primitive operations in
: those child packages: 

Since this couple of recursive types is strictly internal to the
abstraction "employee and his office", I recommend that they be
declared as abstract and hidden in the private part of the parent
package (Yes, this compiles with GNAT):

    package Company is
    private
      type Employee_Parent;
      type Employee_Pointer is access all Employee_Parent'Class;

      type Office_Parent;
      type Office_Pointer is access all Office_Parent'Class;


      type Employee_Parent is abstract tagged
        record
          Its_Occupied_Office : Office_Pointer;
        end record;

      type Office_Parent is abstract tagged
        record
          Its_Occupying_Employee : Employee_Pointer;
        end record;
    end Company;

The rest of Norm's solution doesn't change; the couple of abstractions
Employee and Office is declared in two child units of Company.  To the
client programmer, the encapsulation is strong but the relationship
between the two abstractions is quite apparent because they are exported
by the Company.* subsystem (a hierarchy of packages).

-- 
Magnus Kempe		"I know not what course others may take, but as for me,
Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  9:48   ` Magnus Kempe
@ 1994-09-29 13:10     ` Magnus Kempe
  1994-09-29 18:05       ` Tucker Taft
  1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
  1994-09-29 13:35     ` John Volan
  1 sibling, 2 replies; 43+ messages in thread
From: Magnus Kempe @ 1994-09-29 13:10 UTC (permalink / raw)


John is still not satisfied that Ada 9X has a clean, elegant solution
to his problem (two abstractions, Employee and Office, have a recursive
dependency--how to model/implement that?).

The example below illustrates that Ada 9X allows you to easily model/
implement abstractions and their relationships--while enforcing and
preserving the concomitant encapsulations.  That's what I call progress!
(Yes, this all compiles with GNAT, a.k.a. "GNU Ada".)


As I wrote in an other message:
-- Since this couple of recursive types is strictly internal to the
-- abstraction "employee and his office", I recommend that they be
-- declared as abstract and hidden in the private part of the parent
-- package.
--------------------------------------------------------------------

package Company is
-- This subsystem exports two abstractions, Employee and Office, which
-- are mutually dependent (in a symmetric, one-to-one relationship).

private
-- the following private declarations set up the 1-1 relationship

  type Employee_Parent;
  type Employee_Reference is access all Employee_Parent'Class;

  type Office_Parent;
  type Office_Reference is access all Office_Parent'Class;

  type Employee_Parent is
    abstract tagged
    record
      Its_Occupied_Office : Office_Reference;
    end record;

  type Office_Parent is
    abstract tagged
    record
      Its_Occupying_Employee : Employee_Reference;
    end record;
end Company;


-- We now declare both abstractions in child units (based on Norm's solution)
-----------------------------------------------------------------------------
with ... ; -- other stuff needed by Employee
package Company.Employees is
  type Employee is
    private;
  type Employee_Pointer is
    access all Employee;

  No_Employee : constant Employee_Pointer := null;
  ... -- Employee subprograms involving that "other stuff"

private
  type Employee is
    new Employee_Parent
    with record
      ... -- various components involving that "other stuff"
    end record;
end Company.Employees;


with ... ; -- other stuff needed by Office
package Company.Offices is
  type Office is
    private;
  type Office_Pointer is
    access all Office;

  No_Office : constant Office_Pointer := null;
  ... -- Office subprograms involving that "other stuff"

private
  type Office is
    new Office_Parent
    with record
      ... -- various components involving that "other stuff"
    end record;
end Company.Offices;


-- we can now declare operations working with both abstractions
---------------------------------------------------------------
with
  Company.Employees, Company.Offices;
package Company.Common is
  function Office_Occupied_By (The_Employee : in Employees.Employee)
    return Offices.Office_Pointer;

  function Employee_Occupying (The_Office : in Offices.Office)
    return Employees.Employee_Pointer;

  procedure Occupy (The_Office   : in Offices.Office_Pointer;
                    The_Employee : in Employees.Employee_Pointer);

  ... -- Employee and Office subprograms working with the relationship
      -- and/or the operations exported by each abstraction.
end Company.Common;


-- Note that the exported pointers are "specific".  The client does not
-- see anything class-wide.  John Volan is afraid that we will have
-- recourse to "Unchecked_Conversion" and/or break Ada's type safety.
-- Not at all.  We need a private child, but that's part of building
-- clean subsystems.
-----------------------------------------------------------------------
private package Company.Employees.Ptrs is
-- helper package which "knows" that Employee is derived from Employee_Parent
  function Office_of (The_Employee : in Employee)
    return Office_Reference;
end Company.Employees.Ptrs;

package body Company.Employees.Ptrs is
  function Office_of (The_Employee : in Employee)
    return Office_Reference is
  begin
    return The_Employee.Its_Occupied_Office;
  end Office_of;
end Company.Employees.Ptrs;


with Company.Employees.Ptrs;
package body Company.Common is
  function Office_Occupied_By (The_Employee : in Employees.Employee)
    return Offices.Office_Pointer is
  begin
    return Offices.Office_Pointer
             (Employees.Ptrs.Office_of (The_Employee));
    -- the type conversion (from one access type to another) involves
    -- a type check: is the designated object of type Offices.Office?
    -- If not, exception Constraint_Error will be raised.
  end Office_Occupied_By;

  -- similar approach for Employee_Occupying and Occupy
  ...
end Company.Common;


-- John Volan expects that the body of each abstraction will be a client
-- of the other abstraction.  No problem.
------------------------------------------------------------------------
with Company.Offices;
package body Company.Employees is
  ... -- bodies of Employee subprograms involving that "other stuff"
      -- and using the operations exported by Office
end Company.Employees;

with Company.Employees;
package body Company.Offices is
  ... -- bodies of Office subprograms involving that "other stuff"
      -- and using the operations exported by Employee
end Company.Offices;


So...

The structure of the system is clearly made for one abstraction Employee
and one abstraction Office.  A future maintainer cannot accidentally
add subclasses of the two abstractions and silently break the common
operations.

The encapsulation of each abstraction is not broken anywhere; rather,
the one-one relationship is abstracted into the parent Company package
and operated upon in the child package Company.Common.

-- 
Magnus Kempe		"I know not what course others may take, but as for me,
Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry


PS: the above code needs a work around for a GNAT bug
(lack of visibility from a grand-child to his grand-parent).

In company-employees.ads amd company-offices.ads, add the following
two lines in each private part:
  subtype Employee_Reference is Company.Employee_Reference;
  subtype Office_Reference is Company.Office_Reference;




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  9:48   ` Magnus Kempe
  1994-09-29 13:10     ` Magnus Kempe
@ 1994-09-29 13:35     ` John Volan
  1994-09-30 20:27       ` Norman H. Cohen
  1994-09-30 22:46       ` Matt Kennel
  1 sibling, 2 replies; 43+ messages in thread
From: John Volan @ 1994-09-29 13:35 UTC (permalink / raw)


MK = Magnus.Kempe@di.epfl.ch (Magnus Kempe) writes:

MK>Norm's solution could be further improved as follows:
MK>
MK>: One variation on this is to declare recursive types meant to serve as the
MK>: parents of types Employee and Office, but to provide no operations for
MK>: these recursive types.  Then, in child packages, declare Employee and
MK>: Office themselves as derived types and declare primitive operations in
MK>: those child packages: 
MK>
MK>Since this couple of recursive types is strictly internal to the
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MK>abstraction "employee and his office", I recommend that they be
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Not true.  The premise of my original question was that the binary association
between Employee and Office is a public feature fully visible to their
clients -- and they could even include each other as clients!  Maybe the
*implementation* of this binary association is strictly internal to the
abstraction (I agree that it should be), but the *existence* of this binary
association should be a visible part of this abstraction.  In other words,
the interface to the Employee class should include some subprogram(s) that
allow(s) a client to fetch the identity of (a pointer to) the associated Office.
Likewise the interface to the Office class should include some subprogram(s)
that allow(s) a client to fetch the identity of (a pointer to) the associated
Employee.  And the interface to both classes should include some subprogram(s)
that allow a client to establish an association between a given Office and a
given Employee, in such a way as to always guarantee the following invariant
assertion:

	For any given Employee "E" and Office "O",
	E occupies O if and only if O is occupied by E.


MK>declared as abstract and hidden in the private part of the parent
MK>package (Yes, this compiles with GNAT):
MK>
MK>    package Company is
MK>    private
MK>      type Employee_Parent;
MK>      type Employee_Pointer is access all Employee_Parent'Class;
MK>
MK>      type Office_Parent;
MK>      type Office_Pointer is access all Office_Parent'Class;
MK>
MK>      type Employee_Parent is abstract tagged
MK>        record
MK>          Its_Occupied_Office : Office_Pointer;
MK>        end record;
MK>
MK>      type Office_Parent is abstract tagged
MK>        record
MK>          Its_Occupying_Employee : Employee_Pointer;
MK>        end record;
MK>    end Company;
MK>
MK>The rest of Norm's solution doesn't change; the couple of abstractions
MK>Employee and Office is declared in two child units of Company.  To the
MK>client programmer, the encapsulation is strong but the relationship
MK>between the two abstractions is quite apparent because they are exported
MK>by the Company.* subsystem (a hierarchy of packages).


Unfortunately, hiding the pointer types within the private part of the parent
prevents them from being visible to clients, even via child packages.  This 
defeats the whole purpose of having a mutually recursive *abstraction* --
that is, something that provides an *interface* to some useful services while 
encapsulating the *implementation* of those services.


MK>-- 
MK>Magnus Kempe		"I know not what course others may take, but as for me,
MK>Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry


-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  1:46   ` John Volan
@ 1994-09-29 13:57     ` Tucker Taft
  1994-09-29 17:20       ` Bjarne Stroustrup <9758-26353> 0112760
  1994-09-29 18:37       ` John Volan
  1994-09-29 18:10     ` R. William Beckwith
  1994-10-03  0:33     ` Cyrille Comar
  2 siblings, 2 replies; 43+ messages in thread
From: Tucker Taft @ 1994-09-29 13:57 UTC (permalink / raw)


In article <1994Sep29.014611.20263@swlvx2.msd.ray.com>,
John Volan <jgv@swl.msd.ray.com> wrote:

> ...
>Let's say some client of Employee asks an Employee object for its Office.  The
>Employee object will give back one of these "opaque" pointers in response.  What
>can the client do with that pointer?  As far as it can see, the Office object
>being pointed to is totally featureless.  Oh, sure, it's possible that this
>Office object is really some subclass of Abstract_Office that does have useful
>features, but this information isn't available to the client.  The only way the
>client could get to that information would be to use an unchecked conversion to 
>change this "opaque" pointer into a pointer to the "non-opaque" Office subclass
>below.  (I think folks in the other languages would call this "downcasting".)
>But this practice breaks type-safety!

Actually, you can "down cast" (aka "narrow") safely in Ada 9X.
There is no need to revert to unchecked conversion.
If you have (a pointer to) a class-wide type, you can explicitly convert
it to (a pointer to) any type "covered" by the class-wide type.
A run-time check is performed as appropriate to make sure what you are
doing is copacetic.  See RM9X-4.6(15, 23, 42, 50);5.0.

For example:

     type T is abstract tagged private;
     type PT is access all T'Class;

   ...

     type T1 is new T with private;
     type PT1 is access all T1'Class;

   ...

     Y : PT;
     X : PT1 := PT1(Y);  -- Run-time check performed, as appropriate

    ...

     function To_T1(A : T'Class) return T1'Class is
     begin
         return T1'Class(A);  -- Run-time check performed, as appropriate
     end To_T1;

This capability of Ada 9X is vaguely related to the "assignment attempt"
of Eiffel, and the dynamic_cast of ANSI/ISO C++-to-be, but manages
to fit quite nicely into the existing Ada 83 concept of (safe) explicit
conversion.

Note that Ada 9X also has a way to check whether a given object
is in a give class before attempting a conversion, as a generalization
of the Ada 83 concept of membership test:

    if A in T1'Class then ...  -- Checks whether "tag" of A indicates
                               -- that it is in class of types rooted at T1.

    if Y = null or else Y.all in T1'Class then ...
           -- Checks that PT1(Y) will succeed, before
	   -- attempting it.

So using access-to-root-abstract-class-wide-type is a viable and safe
approach in Ada 9X, particularly when you "know" there is only
one direct derivative of the root abstract type, and you are
converting to that. 

However, putting two mutually recursive types in the same
package is the traditional Ada way of solving this problem, and
seems preferable to me.  You can preserve modularity by
making these types abstract, while eliminating the need for
explicit type conversions by declaring all of the interesting 
mutually-recursive primitives in this one package.

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  2:12   ` John Volan
@ 1994-09-29 14:01     ` Tucker Taft
  1994-09-29 18:37     ` Norman H. Cohen
  1 sibling, 0 replies; 43+ messages in thread
From: Tucker Taft @ 1994-09-29 14:01 UTC (permalink / raw)


In article <1994Sep29.021209.20769@swlvx2.msd.ray.com>,
John Volan <jgv@swl.msd.ray.com> wrote:

>NHC = ncohen@watson.ibm.com (Norman H. Cohen) writes:
>
>NHC>One variation on this is to declare recursive types meant to serve as the
>NHC>parents of types Employee and Office, but to provide no operations for
>NHC>these recursive types.  Then, in child packages, declare Employee and
>NHC>Office themselves as derived types and declare primitive operations in
>NHC>those child packages: 
>NHC>
>NHC>    with ... ; -- other stuff needed by Employee
>NHC>    with ... ; -- other stuff needed by Office
>NHC>
>NHC>    package Employees_And_Offices is
>NHC>
>NHC>      type Employee_Parent is tagged limited private;
>NHC>      type Employee_Pointer is access all Employee'Class;
>NHC>
>NHC>      type Office_Parent is tagged limited private;
>NHC>      type Office_Pointer is access all Office'Class;
>
>This solution suffers from the same problem as Mark Biggar's suggestion:
>These access types only give clients an "opaque" view of the designated objects.
>The useful primitive operations for these objects won't be declared until we 
>get to the concrete *subclasses* declared later.  So a client would have to
>resort to using a non-typesafe Unchecked_Conversion to "downcast" one of these
>pointers into designating the corresponding concrete subclass.

As mentioned in a previous post, there is no need to use
non-typesafe Unchecked_Conversion.  A safe, checked, explicit "downcast"
(or "narrowing") conversion is permitted in Ada 9X when the operand is 
of a class-wide type, or of an access-to-class-wide type.
See RM9X-4.6(15, 23, 42, 50);5.0.

>-- John Volan
>--                  Company             => "Raytheon Missile Systems Division",
>--                  E_Mail_Address      => "jgv@swl.msd.ray.com",

S. Tucker Taft   stt@inmet.com
Intermetrics, Inc.
Cambridge, MA  02138



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:57     ` Tucker Taft
@ 1994-09-29 17:20       ` Bjarne Stroustrup <9758-26353> 0112760
  1994-09-30  1:38         ` Tucker Taft
  1994-09-29 18:37       ` John Volan
  1 sibling, 1 reply; 43+ messages in thread
From: Bjarne Stroustrup <9758-26353> 0112760 @ 1994-09-29 17:20 UTC (permalink / raw)



stt@dsd.camb.inmet.com (Tucker Taft) writes

 > This capability of Ada 9X is vaguely related to the "assignment attempt"
 > of Eiffel, and the dynamic_cast of ANSI/ISO C++-to-be,

Ah. Hmm. I have noted a tendency to treat newer C++ features as if they were
some sort of science fiction. Features such as dynamic_cast are currently
available in widely-used commercial implementations of C++ as well as being
part of the emerging standard.

I think it would be best to leave out phrases such as ``ANSI/ISO C++-to-be''
as possibly prejudicial. Alternatively, one could start a flamewar by trying
to be fair by applying matching adjectives to Ada9X and Eiffel.

	- Bjarne



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:10     ` Magnus Kempe
@ 1994-09-29 18:05       ` Tucker Taft
  1994-09-30 10:20         ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
  1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
  1 sibling, 1 reply; 43+ messages in thread
From: Tucker Taft @ 1994-09-29 18:05 UTC (permalink / raw)


In article <36eebb$jn5@disunms.epfl.ch>,
Magnus Kempe <Magnus.Kempe@di.epfl.ch> wrote:
> ...
>The example below illustrates that Ada 9X allows you to easily model/
>implement abstractions and their relationships--while enforcing and
>preserving the concomitant encapsulations.  That's what I call progress!
>(Yes, this all compiles with GNAT, a.k.a. "GNU Ada".)

The type conversion toward the end of your example is
illegal (at least according to *our* Ada 9X front end ;-).
Remember GNAT is focusing on compiling correct programs first,
and will eventually enforce all of the nit-picking RM rules,
but for now, just because GNAT compiles something doesn't make it legal ...

In any case, to me it is much simpler to declare abstract
versions of Employee and Office in the same package, with appropriately
mutually recursive (abstract) operations declared there as well.
E.g.:

    package Company is
        type Office is abstract tagged null record;
        type Office_Pointer is access all Office'Class;

        type Employee is abstract tagged null record;
        type Employee_Pointer is access all Employee'Class;

        function Office_Occupied_By(The_Employee : Employee)
          return Office_Pointer is abstract;

        function Employee_Occupying (The_Office : Office)
          return Employee_Pointer is abstract;

        procedure Occupy (The_Office : in Office_Pointer;
                          The_Employee : in Employee_Pointer);
          -- This one we presumably have to implement (though
          -- I'm not sure what the original author had in mind),
          -- perhaps by calling additional abstract primitive operations 
	  -- declared below....
          
      ...

    end Company;

You can then create concrete derivatives of these in separate packages,
preserving complete encapsulation relative to one another (presuming
that is a big deal).  Few if any explicit conversions will be required
with this approach.

>Magnus Kempe		"I know not what course others may take, but as for me,
>Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry

-Tucker Taft   stt@inmet.com
Intermetrics, Inc.
Cambridge, MA  02138

-------------------
P.S. FYI Here is the source code you suggested, with an indication
of where the error occurs:

> ...
>package Company is
>-- This subsystem exports two abstractions, Employee and Office, which
>-- are mutually dependent (in a symmetric, one-to-one relationship).
>
>private
>-- the following private declarations set up the 1-1 relationship
>
> ... [Eliding employee stuff]
>
>  type Office_Parent;
>  type Office_Reference is access all Office_Parent'Class;
>
> ... [Eliding employee stuff]
>
>  type Office_Parent is
>    abstract tagged
>    record
>      Its_Occupying_Employee : Employee_Reference;
>    end record;
>end Company;
>
> ... [Eliding employee stuff]
>
>with ... ; -- other stuff needed by Office
>package Company.Offices is
>  type Office is
>    private;
>  type Office_Pointer is
>    access all Office;
>
>  No_Office : constant Office_Pointer := null;
>  ... -- Office subprograms involving that "other stuff"
>
>private
>  type Office is
>    new Office_Parent
>    with record
>      ... -- various components involving that "other stuff"
>    end record;
>end Company.Offices;
>
>
>-- we can now declare operations working with both abstractions
>---------------------------------------------------------------
>with
>  Company.Employees, Company.Offices;
>package Company.Common is
>  function Office_Occupied_By (The_Employee : in Employees.Employee)
>    return Offices.Office_Pointer;
>
> ... [Eliding employee stuff]
>end Company.Common;
>
> ... [Eliding employee stuff]
>
>with Company.Employees.Ptrs;
>package body Company.Common is
>  function Office_Occupied_By (The_Employee : in Employees.Employee)
>    return Offices.Office_Pointer is
>  begin
>    return Offices.Office_Pointer
>             (Employees.Ptrs.Office_of (The_Employee));
>    -- the type conversion (from one access type to another) involves
>    -- a type check: is the designated object of type Offices.Office?
>    -- If not, exception Constraint_Error will be raised.

***** Here is the problem ****
This type conversion is illegal, since at this point, it is
not visible that Office is derived from Office_Parent.

>  end Office_Occupied_By;
> ...



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  1:46   ` John Volan
  1994-09-29 13:57     ` Tucker Taft
@ 1994-09-29 18:10     ` R. William Beckwith
  1994-10-03  0:33     ` Cyrille Comar
  2 siblings, 0 replies; 43+ messages in thread
From: R. William Beckwith @ 1994-09-29 18:10 UTC (permalink / raw)


John Volan (jgv@swl.msd.ray.com) wrote:

: MAB = mab@dst17.wdl.loral.com (Mark A Biggar) writes:

snip

: ...  The only way the
: client could get to that information would be to use an unchecked conversion to 
: change this "opaque" pointer into a pointer to the "non-opaque" Office subclass
: below.  (I think folks in the other languages would call this "downcasting".)
: But this practice breaks type-safety!

Actually the cool term is now `narrowing'.  Narrowing the access type from 

    access all Abstract_Office'class
    
	to
	
    access all Office'class
    
does not break type saftey.  Since Abstract_Office is abstract, there can be
no objects of type Abstract_Office.  Thus, Abstract_Office'class is always
safely convertable to Office'class.

This issue (we call it the "withing problem") is addressed in the CORBA
IDL to Ada 9X mapping document.  This mapping provides a nice solution
to this issue that is very similiar to Mark's.  The CORBA IDL to C++
mapping document defines C++ classes that are much more complex and
cumbersome (IMHAARO) than non-CORBA C++ classes.  I think the IDL to 
Ada 9X mapping provides a nice way to code Ada 9X even if you're not
using CORBA.

... Bill

-- 
e-mail: Bill.Beckwith@ois.com       |    Team Ada
Objective Interface Systems, Inc.   | dist, full O-O
1895 Preston White Drive, Suite 250 | multithreading
Reston, VA  22091-5448  U.S.A.      |    built in



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  2:12   ` John Volan
  1994-09-29 14:01     ` Tucker Taft
@ 1994-09-29 18:37     ` Norman H. Cohen
  1 sibling, 0 replies; 43+ messages in thread
From: Norman H. Cohen @ 1994-09-29 18:37 UTC (permalink / raw)


In article <1994Sep29.021209.20769@swlvx2.msd.ray.com>,
jgv@swl.msd.ray.com (John Volan) writes: 

|> This solution suffers from the same problem as Mark Biggar's suggestion: 

Mark's solution and mine have a lot in common.  (I should have declared
Employee_Parent and Office_Parent to be abstract, as Mark did with the
corresponding types in his solution, to indicate my intent that there be
no objects of this type.)

|> These access types only give clients an "opaque" view of the designated objects.
|> The useful primitive operations for these objects won't be declared until we
|> get to the concrete *subclasses* declared later.  So a client would have to
|> resort to using a non-typesafe Unchecked_Conversion to "downcast" one of these
|> pointers into designating the corresponding concrete subclass.

You are right.  This problem can be solved by declaring all the
operations of Employee and Office as abstract operations of
Employee_Parent and Office_Parent.  (So making the parent types abstract
isn't just a good idea, it's the law.)  The common operations can be
handled by adding

    function Office_Occupied_By (The_Employee : in Employee_Parent'Class)
      return Office_Pointer;

    function Employee_Occupying (The_Office : in Office_Parent'Class)
      return Employee_Pointer;

    procedure Occupy (The_Office   : in access Office_Parent'Class;
                      The_Employee : in access Employee_Parent'Class);

to Employees_And_Offices as concrete classwide operations.

|> Another issue is whether the concrete subclasses declared below are going to
|> be the *only* subclasses of these abstract classes.  Certainly that's the
|> original intent -- but will a maintainer pay any attention? ;-)

I don't see any problem here.  If the maintainer can come up with another
useful extension to Office_Parent or Employee_Parent, more power to him.
Indeed, type inheritance and child packages are useful in part as a
"structured" way for a maintenance programmer to tailor the facilities in
the original program in a way not anticipated by the original author,
while leaving the original author's source intact.  The correctness of an
OO program should not depend on the absence of subclasses not mentioned
in the original program!

|> Another problem here is that this doesn't really solve the original puzzle I
|> posed:  How do you avoid breaking encapsulation *between* these two classes?
|> These type declarations are private, but since the packages below are all
|> children of this parent package, they have complete visibility to the private
|> part of the parent.  So an Employee subprogram still has license to ignore
|> the public primitives of the Office class and "diddle" with the Office's
|> private components directly.  (And vice versa.)

What you are asking for is impossible.  As I noted yesterday, there is no
way to define, in two separate packages, two types with primitive
operations that operate on the other type, because the package specs are
not allowed to be mutually dependent: 

   - If Offices depends on Employees, for example, Employees can not depend on
     Offices, so there is no way to declare Office_Occupied_By (which
     must be declared in a place where its body can see the
     Its_Occupied_Office record component).

   - If Offices does not depend on Employees, there is no way to declare
     Employee_Occupying (which must be declared in a place where its body
     can see the Its_Occupying_Employee record component).

But, as the song says, that's what friends are for.  The implementations
of mutually recursive types are often so tightly intertwined that it is
not possible to say where one ends and the other begins; in the case of
tightly coupled mutually recursive types there is no point in hiding the
implementation of one from the implementation of the other.  Tightly
coupled mutually recursive types constitute a single abstraction
consisting of two or more types, typically designed by one person and
implemented by one person even if the abstraction is distributed among
several child packages.  When the mutually recursive types are tightly
coupled, the ability of the subprogram bodies for each type single to
reference the full recursive definition is a feature, not a bug.

However, types Office and Employee are not so intimately intertwined.
Each contains one pointer to the other and much more that has nothing to
do with the other type.  Here is an approach that exploits the loose
coupling between the recursive types to hide only the "other stuff": 

   package Employees_And_Offices is

      type Employee_Parent is abstract tagged private;
      type Employee_Pointer is access all Employee_Parent'Class;
       -- Abstract Employee_Parent subprograms involving that
       --     "other stuff"

       type Office_Parent is abstract tagged private;
       type Office_Pointer is access all Office_Parent'Class;
       -- Abstract Office_Parent subprograms involving that
       --     "other stuff"

       -- Concrete nondispatching subprograms involving only the pointers
       --    between the classes: 

       function Office_Occupied_By
          (The_Employee: in Employee_Parent'Class) return Office_Pointer;

       function Employee_Occupying
          (The_Office: in Office_Parent'Class) return Employee_Pointer;

       procedure Occupy
          (The_Office: in Office_Pointer; The_Employee: in Employee_Pointer);

   private

      -- Only those components of Office and Employee that are of mutual
      --    interest: 

      type Employee_Parent is
         record
            Its_Occupied_Office : Office_Pointer;
         end record;

      type Office_Parent is
         record
            Its_Occupying_Employee : Employee_Pointer;
         end record;

   end Employees_And_Offices;

   package body Employees_And_Offices is

       function Office_Occupied_By
          (The_Employee: in Employee_Parent'Class) return Office_Pointer is
       begin
          return The_Employee.Its_Occupied_Office;
       end Office_Occupied_By;

       function Employee_Occupying
          (The_Office: in Office_Parent'Class) return Employee_Pointer is
       begin
          return The_Office.Its_Occupying_Employee;
       end Employee_Occupying;

       procedure Occupy
          (The_Office: in Office_Pointer; The_Employee: in Employee_Pointer) is
       begin
          The_Employee.Its_Occupied_Office := The_Office;
          The_Office.Its_Occupying_Employee := The_Employee;
       end Occupy;

   end Employees_And_Offices;

   package Employees_And_Offices.Employees is
      type Employee is new Employee_Parent with private;
       -- Concrete Employee subprograms involving that
       --     "other stuff"
   private
      type Employee is new Employee_Parent with
         record
            -- various components involving that "other stuff"
         end record;
   end Employees_And_Offices.Employees;

   package Employees_And_Offices.Offices is
      type Office is new Office_Parent with private;
       -- Concrete Office subprograms involving that
       --     "other stuff"
   private
      type Office is new Office_Parent with
         record
            -- various components involving that "other stuff"
         end record;
   end Employees_And_Offices.Offices;


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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:57     ` Tucker Taft
  1994-09-29 17:20       ` Bjarne Stroustrup <9758-26353> 0112760
@ 1994-09-29 18:37       ` John Volan
  1994-09-29 19:34         ` David Weller
  1994-09-30  1:47         ` Tucker Taft
  1 sibling, 2 replies; 43+ messages in thread
From: John Volan @ 1994-09-29 18:37 UTC (permalink / raw)


STT = stt@dsd.camb.inmet.com (Tucker Taft) writes:

STT>In article <1994Sep29.014611.20263@swlvx2.msd.ray.com>,
STT>John Volan <jgv@swl.msd.ray.com> wrote:

[snip my own drivel claiming you need unsafe Unchecked_Conversion to downcast]

STT>Actually, you can "down cast" (aka "narrow") safely in Ada 9X.
STT>There is no need to revert to unchecked conversion.
STT>If you have (a pointer to) a class-wide type, you can explicitly convert
STT>it to (a pointer to) any type "covered" by the class-wide type.
STT>A run-time check is performed as appropriate to make sure what you are
STT>doing is copacetic.  See RM9X-4.6(15, 23, 42, 50);5.0.

[snip good example showing safe downcasting]

STT>This capability of Ada 9X is vaguely related to the "assignment attempt"
STT>of Eiffel, and the dynamic_cast of ANSI/ISO C++-to-be, but manages
STT>to fit quite nicely into the existing Ada 83 concept of (safe) explicit
STT>conversion.
STT>
STT>Note that Ada 9X also has a way to check whether a given object
STT>is in a give class before attempting a conversion, as a generalization
STT>of the Ada 83 concept of membership test:
STT>
STT>    if A in T1'Class then ...  -- Checks whether "tag" of A indicates
STT>                               -- that it is in class of types rooted at T1.
STT>
STT>    if Y = null or else Y.all in T1'Class then ...
STT>           -- Checks that PT1(Y) will succeed, before
STT>	   -- attempting it.
STT>

Er, I think you meant:

        if Y /= null and then Y.all in T1'Class then ...

Or maybe, if you wanted to cover the contrary case first:

        if Y = null or else Y.all not in T1'Class then ...

STT>So using access-to-root-abstract-class-wide-type is a viable and safe
STT>approach in Ada 9X, particularly when you "know" there is only
STT>one direct derivative of the root abstract type, and you are
STT>converting to that. 


Ahh, the light begins to dawn.  I could kick myself -- saw this once in the
RM9X and totally forgot about it.  Should have guessed it was there anyway,
on first principles: "If there's something reasonable you want to do, more than
likely there's a SAFE way to do it in Ada 9X."  (An old saying I just made up.)

So -- this now gives me a safe, systematic technique for coding up object 
classes that are each fully encapsulated in their own packages, yet allowing 
any degree of mutual dependency among them.  I'll outline this technique at
the end of this article, but first ...


STT>However, putting two mutually recursive types in the same
STT>package is the traditional Ada way of solving this problem, and
STT>seems preferable to me.  You can preserve modularity by
STT>making these types abstract, while eliminating the need for
STT>explicit type conversions by declaring all of the interesting 
STT>mutually-recursive primitives in this one package.


I won't argue with your personal preference, but let me point out that this
"traditional" technique will have a difficult time scaling up.  For the
sake of discussion, I deliberately chose a very simple situation involving only
two classes, mutually related in a simple one-to-one association.  But, without
loss of generality, I was looking for a solution that could be systematically
applied to systems comprising many, many object classes, each one participating
in mutually recursive associations with possibly many, many other classes, with
any degree of cardinality (one-to-one, one-to-many, many-to-many, etc). 

Unfortunately, the "traditional" technique you suggest (and which I outlined in
my original post) requires breaking encapsulation between any pair of classes 
that happen to be mutually recursive -- and this effect is transitive!  The end
result would be one huge, monolithic package containing *all* (or perhaps most)
of the classes in a system.  I don't think I need to elaborate on the
detrimental effect that would have on a large, long-term project involving 
continously-changing requirements.  The whole point to packaging was to avoid
monolithic coding techniques.  (There you go again, John, preaching to the
choir -- nay, preaching to the *minister*! :-)

I am an unabashed fanatic of the 1 Package = 1 Type = 1 Class approach, and
making that approach workable was the whole point of this thread.  In part, I'm
influenced by Karl Lieberherr's work on the Demeter method, and his so-called 
"Law of Demeter".  This law more or less asserts that each class in an 
object-oriented system should be completely encapsulated and self-administering.
A class should only "know about" the classes it is immediately associated with,
and only "know about" them in terms of their own encapsulated interfaces.
Moreover, this encapsulation should never be broken down, even if some
functionality of the system requires propagating an algorithm across many
classes of object.  Instead, the algorithm should be realized by a collaborative
interaction among the public subprograms of the various classes.  I think this 
is the essence of the whole object-oriented paradigm.


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


Well, thanks to you and to everyone else who contributed to this thread!

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------

As promised, here's an outline of my coding strategy:

1. "Forward declare" the classes by writing a "root" package for each, 
containing a featureless abstract tagged type and an accompanying 
access-to-classwide-type.  This establishes a way of manipulating the
*identities* of objects without having to know their *structure* in advance:

    package Employee is
      type Object is abstract tagged limited null record;
      type Pointer is access all Object'Class;
      None : constant Pointer := null;
    end Employee;

    package Office is
      type Object is abstract tagged limited null record;
      type Pointer is access all Object'Class;
      None : constant Pointer := null;
    end Office;

    ... root packages for other object classes in the problem domain


2. Flesh out the "actual" abstraction for each class, in a package spec that is
a child of its corresponding "root" package.  Derive an "actual" concrete
type from the root abstract tagged type.  Wherever the class in question visibly
supports an association with another class, import the other class's root 
package via a "with" clause, and use the corresponding pointer-to-abstract type:

(NOTE: Where two classes are mutually recursive, *both* can provide primitive
subprograms to support the association between them.  Where necessary,
these subprograms can be ... mutually recursive!)

    ----------------------------------------------------------------------

    with Office;
    with ... ; -- other stuff, possibly including other class root packages

    package Employee.Actual is

      type Object is new Employee.Object with private;
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- subprograms involving other stuff

      -- Public support for association with Office:

      function Office_Occupied_By
      ( The_Employee : in Employee.Actual.Object'Class ) return Office.Pointer;

      procedure Occupy_Office
      ( The_Employee : in out Employee.Actual.Object'Class;
        New_Office   : in     Office.Pointer );
      -- Mutually recursive with Office.Actual.Accomodate_Employee

      procedure Vacate_Office
      ( The_Employee : in out Employee.Actual.Object'Class );
      -- Mutually recursive with Office.Actual.Evict_Employee

      ...

    private
      type Object is new Employee.Object with
        record
          ... -- components involving other stuff
          Its_Occupied_Office : Office.Pointer;
        end record;
    end Employee.Actual;

    ----------------------------------------------------------------------

    with Employee;
    with ... ; -- other stuff, possibly including other class root packages

    package Office.Actual is

      type Object is new Office.Object with private;
      type Pointer is access all Object'Class;
      None : constant Pointer := null;

      ... -- subprograms involving other stuff

      -- Public support for association with Employee:

      function Employee_Occupying
      ( The_Office : in Office.Actual.Object'Class ) return Employee.Pointer;

      procedure Accomodate_Employee
      ( The_Office   : in out Office.Actual.Object'Class;
        New_Employee : in     Employee.Pointer );
      -- Mutually recursive with Employee.Actual.Occupy_Office

      procedure Evict_Employee
      ( The_Office : in out Office.Actual.Object'Class );
      -- Mutually recursive with Employee.Actual.Vacate_Office

      ...

    private
      type Object is new Office.Object with
        record
          ... -- components involving other stuff
          Its_Occupying_Employee : Employee.Pointer;
        end record;
    end Office.Actual;

    ----------------------------------------------------------------------

    ... "actual" packages for other classes in the problem domain


3. Implement the "actual" abstraction for each class, within the corresponding
child package body.  Wherever the class in question needs access to the full
abstraction of some other class in order to support an association with that
class, import the other class's "actual" package via a "with" clause in the
package body.  Then, wherever the subprograms must interact with a specific
other-class object, downcast the access-to-abstract-root-type into an
access-to-actual-type.  (No explicit validity check is needed -- we'll take
it as an assertion of our design that all designated objects will be of the
correct actual types, and rely on Ada 9X's implicit checks to detect any
inadvertent coding errors.  The run-time cost of these implicit checks can,
if necessary, be eliminated from the final fielded system by using an
appropriate suppressing pragma, once full testing is complete):

    ----------------------------------------------------------------------

    with Office.Actual;
    with ... ; -- actual packages for other classes

    package body Employee.Actual is

      ... -- subprogram bodies involving other stuff

      -- Public support for association with Office:

      function Office_Occupied_By
      ( The_Employee : in Employee.Actual.Object'Class ) 
        return Office.Pointer is
      begin
        return The_Employee.Its_Occupied_Office;
      end Office_Occupied_By;
        
      procedure Occupy_Office
      ( The_Employee : in out Employee.Actual.Object'Class;
        New_Office   : in     Office.Pointer )
      is
        use type Office.Pointer;
      begin
        if New_Office /= Office.None and then
           New_Office /= The_Employee.Its_Occupied_Office 
        then
          Employee.Actual.Vacate_Office (The_Employee);
          The_Employee.Its_Occupied_Office := New_Office;
          Office.Actual.Accomodate_Employee
          ( The_Office   => Office.Actual.Pointer(New_Office).all,
            New_Employee => The_Employee'Access );
        end if;
      end Occupy_Office;

      procedure Vacate_Office
      ( The_Employee : in out Employee.Actual.Object'Class )
      is
        Old_Office : constant Office.Pointer := 
          The_Employee.Its_Occupied_Office;
        use type Office.Pointer;
      begin
        if Old_Office /= Office.None then
          The_Employee.Its_Occupied_Office := Office.None;
          Office.Actual.Evict_Employee
          ( The_Office => Office.Actual.Pointer(Old_Office).all );
        end if;
      end Vacate_Office;

      ...

    end Employee.Actual;

    ----------------------------------------------------------------------

    with Employee.Actual;
    with ... ; -- actual packages for other classes

    package body Office.Actual is

      ... -- subprogram bodies involving other stuff

      -- Public support for association with Employee:

      function Employee_Occupying
      ( The_Office : in Office.Actual.Object'Class ) return Employee.Pointer is
      begin
        return The_Office.Its_Occupying_Employee;
      end Employee_Occupying;
        
      procedure Accomodate_Employee
      ( The_Office   : in out Office.Actual.Object'Class;
        New_Employee : in     Employee.Pointer )
      is
        use type Employee.Pointer;
      begin
        if New_Employee /= Employee.None and then
           New_Employee /= The_Office.Its_Occupying_Employee 
        then
          Office.Actual.Evict_Employee (The_Office);
          The_Office.Its_Occupying_Employee := New_Employee;
          Employee.Actual.Occupy_Office
          ( The_Employee => Employee.Actual.Pointer(New_Employee).all,
            New_Office   => The_Office'Access );
        end if;
      end Accomodate_Employee;

      procedure Vacate_Employee
      ( The_Office : in out Office.Actual.Object'Class )
      is
        Old_Employee : constant Employee.Pointer := 
          The_Office.Its_Occupying_Employee;
        use type Employee.Pointer;
      begin
        if Old_Employee /= Employee.None then
          The_Office.Its_Occupying_Employee := Employee.None;
          Employee.Actual.Vacate_Office
          ( The_Employee => Employee.Actual.Pointer(Old_Employee).all );
        end if;
      end Vacate_Employee;

      ...

    end Office.Actual;

    ----------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 18:37       ` John Volan
@ 1994-09-29 19:34         ` David Weller
  1994-09-30 22:13           ` John Volan
  1994-09-30  1:47         ` Tucker Taft
  1 sibling, 1 reply; 43+ messages in thread
From: David Weller @ 1994-09-29 19:34 UTC (permalink / raw)


In article <1994Sep29.183749.7489@swlvx2.msd.ray.com>,
John Volan <jgv@swl.msd.ray.com> wrote:
>I am an unabashed fanatic of the 1 Package = 1 Type = 1 Class approach, and
>making that approach workable was the whole point of this thread.  

Well, to each their own.  Let's just say I'm on the other end of that
spectrum (no, more to the point, I simply don't advocate
class=package, although there's many times that works).  In my years
of developing software, I have yet to be swayed by arguments of "dual
associations" needing to be represented in classes.  I can see such a
need in databases, but I've just not encountered it in programming.
That's not saying they don't come up during analysis, but that their
final representation generally requires less gymnastics than some of
the interesting examples that have been discussed.  

I guess I can just be counted out of the class=module camp :-/

> In part, I'm
>influenced by Karl Lieberherr's work on the Demeter method, and his so-called 
>"Law of Demeter".  This law more or less asserts that each class in an 
>object-oriented system should be completely encapsulated and self-administering.
>A class should only "know about" the classes it is immediately associated with,
>and only "know about" them in terms of their own encapsulated interfaces.
>Moreover, this encapsulation should never be broken down, even if some
>functionality of the system requires propagating an algorithm across many
>classes of object.  Instead, the algorithm should be realized by a collaborative
>interaction among the public subprograms of the various classes.  I think this 
>is the essence of the whole object-oriented paradigm.
>

Is it appropriate at this point to shudder uncontrollably? :-)

>Well, thanks to you and to everyone else who contributed to this thread!
>
In spite of my disagreement, I think you brought up an interesting
topic.



-- 
Proud (and vocal) member of Team Ada! (and Team OS/2)        ||This is not your
   	      Ada -- Very Cool.  Doesn't Suck.               ||  father's Ada 
For all sorts of interesting Ada tidbits, run the command:   ||________________
"finger dweller@starbase.neosoft.com | more" (or e-mail with "finger" as subj.)
   ObNitPick: Spelling Ada as ADA is like spelling C++ as CPLUSPLUS. :-) 



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 17:20       ` Bjarne Stroustrup <9758-26353> 0112760
@ 1994-09-30  1:38         ` Tucker Taft
  1994-09-30 12:33           ` Bjarne Stroustrup <9758-26353> 0112760
  0 siblings, 1 reply; 43+ messages in thread
From: Tucker Taft @ 1994-09-30  1:38 UTC (permalink / raw)


In article <CwwIuJ.1ny@alice.att.com>,
Bjarne Stroustrup <9758-26353> 0112760 <9758-26353> wrote:
>
>stt@dsd.camb.inmet.com (Tucker Taft) writes
>
> > This capability of Ada 9X is vaguely related to the "assignment attempt"
> > of Eiffel, and the dynamic_cast of ANSI/ISO C++-to-be,
>
>Ah. Hmm. I have noted a tendency to treat newer C++ features as if they were
>some sort of science fiction. Features such as dynamic_cast are currently
>available in widely-used commercial implementations of C++ as well as being
>part of the emerging standard.
>
>I think it would be best to leave out phrases such as ``ANSI/ISO C++-to-be''
>as possibly prejudicial. Alternatively, one could start a flamewar by trying
>to be fair by applying matching adjectives to Ada9X and Eiffel.

Sorry about that.  It seemed wrong to write ANSI/ISO C++; is there a version
number like 3.0 or 4.0 that identifies the set of features expected for
ISO C++ (and is already in some current C++ compilers)? I just wanted those 
non-C++ folks out there to know that dynamic_cast is a feature that 
is anticipated in the ANSI/ISO C++, and might not be familiar to 
those of us who have only read older C++ reference manuals.

>	- Bjarne

-Tuck



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 18:37       ` John Volan
  1994-09-29 19:34         ` David Weller
@ 1994-09-30  1:47         ` Tucker Taft
  1994-09-30 13:30           ` John Volan
  1 sibling, 1 reply; 43+ messages in thread
From: Tucker Taft @ 1994-09-30  1:47 UTC (permalink / raw)


In article <1994Sep29.183749.7489@swlvx2.msd.ray.com>,
John Volan <jgv@swl.msd.ray.com> wrote:

>STT>    if Y = null or else Y.all in T1'Class then ...
>STT>           -- Checks that PT1(Y) will succeed, before
>STT>	   -- attempting it.
>STT>
>
>Er, I think you meant:
>
>        if Y /= null and then Y.all in T1'Class then ...
>
>Or maybe, if you wanted to cover the contrary case first:
>
>        if Y = null or else Y.all not in T1'Class then ...

No, actally I meant what I wrote ;-).  If Y = null, then the conversion
always succeeds, since all (named) access (sub)types include null
as a legal value of the (sub)type.

>I am an unabashed fanatic of the 1 Package = 1 Type = 1 Class approach, and
>making that approach workable was the whole point of this thread.  

I'm not.  In my view packaging is an additional dimension
of flexibility that should be used (but not abused ;-).
Packages are for grouping logically related entities,
and it is fine if a single package includes two closely linked types.

But I understand some of the advantages of the other approach as well...
Vive la difference (but not on *my* project ;-).

>-- John Volan
>--                  Company             => "Raytheon Missile Systems Division",
>--                  E_Mail_Address      => "jgv@swl.msd.ray.com",

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.?
  1994-09-29 18:05       ` Tucker Taft
@ 1994-09-30 10:20         ` Magnus Kempe
  1994-09-30 13:22           ` Tucker Taft
  0 siblings, 1 reply; 43+ messages in thread
From: Magnus Kempe @ 1994-09-30 10:20 UTC (permalink / raw)


stt@spock.camb.inmet.com (Tucker Taft) writes:
: 
: The type conversion toward the end of your example is
: illegal (at least according to *our* Ada 9X front end ;-).

Damn.  Thanks.  Here's a fix, which is also accepted by GNAT :-)
Is your Ada 9X front end a program, or a human?

private
package Company.Offices.Ptrs is
  function Ptr (The_Office : Office_Reference)
    return Office_Pointer;
end Company.Offices.Ptrs;

package body Company.Offices.Ptrs is
  function Ptr (The_Office : Office_Reference)
    return Office_Pointer is
  begin
    -- here we "see" that Office is derived from Office_Parent
    return Office_Pointer (The_Office);
  end Ptr;
end Company.Offices.Ptrs;

with Company.Employees.Ptrs;
with Company.Offices.Ptrs;
package body Company.Common is
  function Office_Occupied_By (The_Employee : in Employees.Employee)
    return Offices.Office_Pointer is
  begin
    return Offices.Ptrs.Ptr (Employees.Ptrs.Office_of (The_Employee));
  end Office_Occupied_By;
  ...
end Company.Common;

: In any case, to me it is much simpler to declare abstract
: versions of Employee and Office in the same package, with appropriately
: mutually recursive (abstract) operations declared there as well.

I agree, but John Volan wanted something else...  My solution completely
avoids showing abstract and/or class-wide stuff to the client.

-- 
Magnus Kempe		"I know not what course others may take, but as for me,
Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-30  1:38         ` Tucker Taft
@ 1994-09-30 12:33           ` Bjarne Stroustrup <9758-26353> 0112760
  0 siblings, 0 replies; 43+ messages in thread
From: Bjarne Stroustrup <9758-26353> 0112760 @ 1994-09-30 12:33 UTC (permalink / raw)




stt@dsd.camb.inmet.com (Tucker Taft) writes

 > Bjarne Stroustrup <9758-26353> 0112760 <9758-26353> wrote:
 > >
 > >stt@dsd.camb.inmet.com (Tucker Taft) writes
 > >
 > > > This capability of Ada 9X is vaguely related to the "assignment attempt"
 > > > of Eiffel, and the dynamic_cast of ANSI/ISO C++-to-be,
 > >
 > >Ah. Hmm. I have noted a tendency to treat newer C++ features as if they were
 > >some sort of science fiction. Features such as dynamic_cast are currently
 > >available in widely-used commercial implementations of C++ as well as being
 > >part of the emerging standard.
 > >
 > >I think it would be best to leave out phrases such as ``ANSI/ISO C++-to-be''
 > >as possibly prejudicial. Alternatively, one could start a flamewar by trying
 > >to be fair by applying matching adjectives to Ada9X and Eiffel.
 > 
 > Sorry about that.  It seemed wrong to write ANSI/ISO C++; is there a version
 > number like 3.0 or 4.0 that identifies the set of features expected for
 > ISO C++ (and is already in some current C++ compilers)? I just wanted those 
 > non-C++ folks out there to know that dynamic_cast is a feature that 
 > is anticipated in the ANSI/ISO C++, and might not be familiar to 
 > those of us who have only read older C++ reference manuals.

Yes. It is hard to know exactly how to refer to these things. I generally use
a phrase like ``in the upcoming ANSI/ISO standard'' when I want specifically
to refer to standards stuff. I tend to refer to features introduced over the
last 3 or so years as ``new features'' and for each try to point out where it
has been implemented and whether it has been made available commercially.

I guess that is too elaborate, and I shouldn't have been grumpy about your
posting. Sorry. It is indeed a pity that there is no generally accepted numbering,
but with a dozen or so implementors out there it just doesn't seem feasible
to refer to C++ with all the ANSI/ISO features as, say, C++ v5.

The most current generally accessible source is my ``The Design and Evolution
of C++'' book.

	- Bjarne




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.?
  1994-09-30 10:20         ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
@ 1994-09-30 13:22           ` Tucker Taft
  0 siblings, 0 replies; 43+ messages in thread
From: Tucker Taft @ 1994-09-30 13:22 UTC (permalink / raw)


In article <1994Sep30.111050@di.epfl.ch>,
Magnus Kempe <Magnus.Kempe@di.epfl.ch> wrote:

>stt@spock.camb.inmet.com (Tucker Taft) writes:
>: 
>: The type conversion toward the end of your example is
>: illegal (at least according to *our* Ada 9X front end ;-).
>
>Damn.  Thanks.  Here's a fix, which is also accepted by GNAT :-)
>Is your Ada 9X front end a program, or a human?

Our Ada 9X front end is a program.  In fact, when it complained 
about your source, I initially presumed our front end was wrong.
But instead, it was the human who was wrong.  I guess our front
end is doing pretty well now that it has surpassed the MRT
in being able to compile Ada 9X ;-).

In any case, our front end likes your fix.

> ...

>-- 
>Magnus Kempe		"I know not what course others may take, but as for me,
>Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry

-Tucker Taft   stt@inmet.com



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-30  1:47         ` Tucker Taft
@ 1994-09-30 13:30           ` John Volan
  0 siblings, 0 replies; 43+ messages in thread
From: John Volan @ 1994-09-30 13:30 UTC (permalink / raw)


stt@dsd.camb.inmet.com (Tucker Taft) writes:

>No, actally I meant what I wrote ;-).  If Y = null, then the conversion
>always succeeds, since all (named) access (sub)types include null
>as a legal value of the (sub)type.

I stand corrected.  (Wow, showing your ignorance again, John, huh? :-)

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

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:35     ` John Volan
@ 1994-09-30 20:27       ` Norman H. Cohen
  1994-10-01  1:47         ` John Volan
  1994-09-30 22:46       ` Matt Kennel
  1 sibling, 1 reply; 43+ messages in thread
From: Norman H. Cohen @ 1994-09-30 20:27 UTC (permalink / raw)


In article <1994Sep29.133526.2134@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com
(John Volan) writes: 

|> Not true.  The premise of my original question was that the binary association
|> between Employee and Office is a public feature fully visible to their
|> clients -- and they could even include each other as clients!  Maybe the
|> *implementation* of this binary association is strictly internal to the
|> abstraction (I agree that it should be), but the *existence* of this binary
|> association should be a visible part of this abstraction.  In other words,
|> the interface to the Employee class should include some subprogram(s) that
|> allow(s) a client to fetch the identity of (a pointer to) the associated Office.
|> Likewise the interface to the Office class should include some subprogram(s)
|> that allow(s) a client to fetch the identity of (a pointer to) the associated
|> Employee.  And the interface to both classes should include some subprogram(s)
|> that allow a client to establish an association between a given Office and a
|> given Employee, in such a way as to always guarantee the following invariant
|> assertion: 
|>
|>      For any given Employee "E" and Office "O",
|>      E occupies O if and only if O is occupied by E.
...
|> Unfortunately, hiding the pointer types within the private part of the parent
|> prevents them from being visible to clients, even via child packages.  This
|> defeats the whole purpose of having a mutually recursive *abstraction* --
|> that is, something that provides an *interface* to some useful services while
|> encapsulating the *implementation* of those services.

The five lines just above seem to contradict your earlier statement that
the pointer types (the imnplementation of the binary association between
offices and employees) should be private, so I'll assume you really meant
what you said the first time and not the second time.  Pasting together
bits and pieces of solutions proposed by Mark Biggar, Magnus Kempe, and
me, isn't the following all that you're really looking for?

   package Employees_And_Offices is

      type Employee_Parent is abstract tagged private;
      type Employee_Pointer is access Employee_Parent'Class;

      type Office_Parent is abstract tagged private;
      type Office_Pointer is access Office_Parent'Class;

      -- Nondispatching  operations concerned with the relationship
      --    between offices and employees: 

      function Office_Occupied_By
         (The_Employee: Employee_Parent'Class) return Office_Pointer;

      function Employee_Occupying
         (The_Office: in Office_Parent'Class) return Employee_Pointer;

      procedure Occupy
         (The_Employee : in Employee_Pointer;
          The_Office   : in Office_Pointer);

   private

      type Employee_Parent is tagged
         record
            Its_Occupied_Office: Office_Pointer;
         end record;

      type Office_Parent is tagged
         record
            Its_Occupying_Employee: Employee_Pointer;
         end record;

   end Employees_And_Offices;


   package Employees_And_Offices.Employees is

      type Employee is new Employee_Parent with private;

      [Operations concerned with the Employee "other stuff"]

   private

      type Employee is new Employee_Parent with
         record
            [the other stuff]
         end record;

   end Employees_And_Offices.Employees;


   package Employees_And_Offices.Offices is

      type Office is new Office_Parent with private;

      [Operations concerned with the Office "other stuff"]

   private

      type Office is new Office_Parent with
         record
            [the other stuff]
         end record;

   end Employees_And_Offices.Offices;

Employee and Office can each see the other's links to itself, but each
type's "other stuff" is hidden from the other.  Outside clients can't
even see the links, just the subprograms for manipulating the links.

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 19:34         ` David Weller
@ 1994-09-30 22:13           ` John Volan
  1994-10-02  3:31             ` Andrew Lees
  0 siblings, 1 reply; 43+ messages in thread
From: John Volan @ 1994-09-30 22:13 UTC (permalink / raw)


Hmm.  I guess when I started this thread, I didn't really make it clear exactly
what I was asking for.  Let me see if I can explain it better, taking the
expression "a picture is worth a thousand words" to heart (but keeping the
thousand words, too :-)  (And there's a side-issue or two in this...)

THE IDENTITY/INTERFACE/IMPLEMENTATION TRICHOTOMY
------------------------------------------------

If I were to draw a Rumbaugh-oid Object Model depicting a requirements
analysis of the original problem I posed, it might look something like this
(highly, highly, schematized):

                        +----+  A1,2  +----+
                        | C1 |--------| C2 |
                        +----+        +----+

Two classes, related to each other by a single one-to-one association.

Additionally, I stated that the functional requirements of the problem demanded
that this association be traversable in both directions.  So, as one possible
design to satisfy these requirements, I proposed making both classes 
"responsible" for the association, by having them point to each other:

                        +----+        +----+
                        | C1 |        | C2 |
                        | o2--------->|    |
                        |    |<---------o1 |
                        +----+        +----+

Not the only possible solution, but a reasonable one.

This solution entails a cyclic dependency between the two classes.
I very much wanted to preserve the notion of 1 Package = 1 Class, but cyclic
dependency presents a dilemma in Ada.  You cannot, of course, have two packages
whose interfaces (specifications) both import ("with") each other.  You
can have their *implementations* (package bodies) import each other's 
interfaces, no problem, but the interfaces themselves can't be mutually 
dependent.

But I realized that, in terms of object-oriented classes, it wasn't really a
matter of having *interfaces* import each other.  To define the interface of one
class you don't *really* need to know the whole interface of the other class.  
All you really need to establish is that the other class *exists*, and that you
can express the *identities* (pointers, let's say) of objects of that other 
class.  In other words, you only need to "forward declare" the other class.  
Once that's done, you can get on with declaring the type and the subprogram
specs for your class.  It's only when you get around to the *implementation* 
(package body) of your class that you really need visibility to the interface 
of the other class.  I really see this as a three-tier system of dependencies
comprising class identity, class interface, and class implementation.  I call
this the "Identity/Interface/Implementation Trichotomy" (is there already
another term for this out there...?)

              +-------------------+           +-------------------+
              | C1 Class Identity |<--_   _-->| C2 Class Identity |
              +-------------------+    \ /    +-------------------+
                                        X
              +-------------------+    / \    +-------------------+
              |    C1 Class       |___/   \___|    C2 Class       |
              |    Interface      |<--_   _-->|    Interface      |
              +-------------------+    \ /    +-------------------+
                                        X
              +-------------------+    / \    +-------------------+
              |        C1         |___/   \___|        C2         |
              |      Class        |           |      Class        |
              |  Implementation   |           |  Implementation   |
              +-------------------+           +-------------------+

                       (Where -----> indicates dependency)

So my original question was:  How can you do this with Ada 9X packages and
tagged types?  Of course, Ada packaging directly supports only two tiers of 
this trichotomy: interface and implementation.  However, using abstract tagged 
types and safe downcasting, I was able to come up with a scheme to simulate 
this three-tier idea, which I outlined it a couple posts ago.  (It's not 
without its costs, mind you.  Maybe there's a better way to do this.  If anyone
can come up with one, I'd really appreciate it!)

(Interestingly enough, I think Eiffel implicitly supports this trichotomy.
At least, as far as I can tell (not actually having programmed in Eiffel) it
looks like you can have two class texts that mention each other in a way
that generates a cyclic dependency.  Anyone out there in Eiffel-land care to
comment?)

Now, I grant you, if it were only a matter of a couple of classes and a single 
association, it would be perfectly reasonable to argue that these classes are
so intimately related that they really form a single irreducible abstraction.
If this were all there were too it, then, yes, you might as well couple both
of these classes within a single package, and forget about decoupling them
from each other.  Other folks on this thread have proposed various schemes,
all very elegant and workable, that seem to me to share this common assumption.
Even where great pains are taken to reduce the coupling to an abstraction,
and hide it away in private parts, the coupling is still there.  Well, maybe
that's okay.

But what if two classes and one association wasn't all there was to it?  
What if, instead of this:

                        +----+  A1,2  +----+
                        | C1 |--------| C2 |
                        +----+        +----+

the requirements picture was really more like *this*:

                      `                 '
                       `               '
                         \            /
                        +----+     +----+
                  - - --| C7 |-----| C8 |             .     '
               +----+   +----+     +----+    '  `     .    '             '
        - - ---| C6 |__   |       /     \   '    `    .   '            '
               +----+  \  |  ____/      |  /       \  |  /            /
                        \ | /           | /         \ | /            /
          +----+  A5,1  +----+  A1,2  +----+  A2,3  +----+  A3,4  +----+
    - - --| C5 |--------| C1 |--------| C2 |--------| C3 |--------| C4 |-- - -
          +----+        +----+\       +----+        +----+        +----+
               \         /  \  \_____/__              |             |
                \       /    \      /   \             .             .
                 \+----+      \+----+    +----+       .             .
            - - --| C9 |-------| C10|----| C11|--- - - -
                  +----+       +----+    +----+       .
                     |          /  \       |
                     .         '    `      .
                     .        '      `     .

(Imagine this semantic network spreading out beyond the edges of your screen :-)
I gave up trying to label every association, but I think you get the picture.)

Let's assume that the nature of the application requires being able to traverse
*all* these binary associations in either direction.  What do we do now?  What
if there are dozens of classes in the model?  Hundreds?  Do we say: "Well, then,
if these classes are *all* so tightly related, then we must declare *all* of 
them in a single package."  And, based on the common software-management 
practice of 1 Package = 1 Engineer, do we also say, "Our requirements model
is bound to change continuously over the course of this project.  Therefore,
we shall have the maintainer of this very important package (upon which all
else depends) work very hard to keep it up-to-date.  Very, _very_ hard.
Moreover, let's also keep him busy making sure all the other packages in the
system recompile successfully every time he changes his package." :-)

(Note to Dave Weller:  Is it appropriate at this point for me to shudder
uncontrollably? ;-)

Okay, you might argue John's just being paranoid, real-world applications are
never all *that* complex.

But perhaps someday they will be.  Perhaps some already are, but they're just
not being done in Ada.

You might argue that even if the semantic network for a problem domain were
really that complex, you shouldn't have to worry that much about mutual 
dependency because real-world applications never demand all *that* much
bi-directional traversal.

But perhaps someday they will.  Perhaps some already do, but they're just not
being done in Ada.

Well, I for one want to be able to tackle them, without a lot of pain, in _Ada_!

(Gosh, I began to sound like GA there for them moment ... no offense, GA!  :-)

--------------------------------------------------------------------------------

IN THE "ON THE OTHER HAND" DEPARTMENT
-------------------------------------

Alright, as I already said, the design I proposed is not the only possible one.
Instead of making either or both of the two classes responsible for the
association:

                        +----+        +----+
                        | C1 |        | C2 |
                        | o2--------->|    |
                        |    |<---------o1 |
                        +----+        +----+

we could place the responsibility for the association entirely within a third
component:

                            +--------+
                            | A(1,2) |
                  +----+    |        |    +----+
                  | C1 |<-----o1  o2----->| C2 |
                  +----+    |        |    +----+
                            +--------+

This "association" package would probably contain some kind of dual hash table,
mapping C1-pointers to C2-pointers, and vice versa. With good modern hashing
techniques, lookups can be quite efficient.  Not as fast as a single pointer
dereference, but "good enough" (O(1) time complexity, amortized).  The nice
thing about this solution is that it totally decouples the classes from having
to know anything about the association.  Adding new associations to a model
has very little ripple effect, if any.  Moreover, this kind of thing is a prime
candidate for writing generically.  Given the nature of my premise (lots
of classes, lots of associations) such a generic would get a *lot* of reuse.

(Thanks to Matt Kennel for reminding me of this scheme.)

Ultimately, this seems to be an excellent way to go -- but does anybody
see any problems with this?  How about if an object instance (of any class)
needs to be deleted?  If the object itself has no knowledge of the associations
it participates in, who is going to make sure that all of these "association
tables" will be notified of that particular object's disappearance?  More
generally, consider any event, affecting an object's state, that also requires
traversing its associations, to propagate that event to other objects.  If the
object itself is not going to be responsible for doing that ... then who,
precisely, will?

Hmm ... maybe an object class *can* know about its associations after all:
What if the body of the class package imported the association package ...?
Hmmm ... got to give this more thought ...

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:35     ` John Volan
  1994-09-30 20:27       ` Norman H. Cohen
@ 1994-09-30 22:46       ` Matt Kennel
  1994-10-01  2:11         ` John Volan
  1 sibling, 1 reply; 43+ messages in thread
From: Matt Kennel @ 1994-09-30 22:46 UTC (permalink / raw)


John Volan (jgv@swl.msd.ray.com) wrote:
: Not true.  The premise of my original question was that the binary association
: between Employee and Office is a public feature fully visible to their
: clients -- and they could even include each other as clients!  Maybe the
: *implementation* of this binary association is strictly internal to the
: abstraction (I agree that it should be), but the *existence* of this binary
: association should be a visible part of this abstraction.  In other words,
: the interface to the Employee class should include some subprogram(s) that
: allow(s) a client to fetch the identity of (a pointer to) the associated Office.
: Likewise the interface to the Office class should include some subprogram(s)
: that allow(s) a client to fetch the identity of (a pointer to) the associated
: Employee.  And the interface to both classes should include some subprogram(s)
: that allow a client to establish an association between a given Office and a
: given Employee, in such a way as to always guarantee the following invariant
: assertion:

: 	For any given Employee "E" and Office "O",
: 	E occupies O if and only if O is occupied by E.


How about this kind solution?  I'm going to have to write in ersatz
Eiffel/Sather:

class TWO_WAY_RELATION{T1,T2} is
      private map_1_to_2:MAP{T1,T2}; -- MAP{T1,T2} is a hash table
			             -- data structure that finds T2's given
				     -- T1's as keys.
      private map_2_to_1:MAP{T2,T1};

      create is
	map_1_to_2 := #MAP{T1,T2};
	map_2_to_1 := #MAP{T1,T2}; end;

      add_pair(x:T1,y:T2) is
	 map_1_to_2.insert(x,y);
	 map_2_to_1.insert(y,x);
      end;

      find_one_given_two(y:T2):T1 is
	return map_2_to_1.find(y); end;

      find_one_given_two(x:T1):T2 is
	return map_1_to_2.find(x); end;

end; -- class TWO_WAY_RELATION

class EMPLOYEE is
	private shared mapper:TWO_WAY_RELATION{EMPLOYEE,OFFICE};
		-- shared so all instances access the same object

	find_my_office : OFFICE is
		mapper.find_two_given_one(self); end;

	set_my_office(o:OFFICE) is
		mapper.add_pair(self,o); end;

end; -- EMPLOYEE

class OFFICE is
	private shared mapper:TWO_WAY_RELATION{EMPLOYEE,OFFICE}
		-- shared so all instances access the same object

	find_my_employee : OFFICE is
		mapper.find_one_given_two(self); end;

	set_my_employee(e:EMPLOYEE) is
		mapper.add_pair(e,self); end;
end; -- OFFICE

---

Now, both Office and Employee present interfaces that can find
and set each one's counterpart.  Neither knows how to do this
except through the 'mapper' object, which presents a clean interface
for managing pairs.

Comments?

--
-Matt Kennel  		mbk@inls1.ucsd.edu
-Institute for Nonlinear Science, University of California, San Diego
-*** AD: Archive for nonlinear dynamics papers & programs: FTP to
-***     lyapunov.ucsd.edu, username "anonymous".



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29 13:10     ` Magnus Kempe
  1994-09-29 18:05       ` Tucker Taft
@ 1994-10-01  1:24       ` Adam Beneschan
  1994-10-01 12:01         ` Magnus Kempe
                           ` (2 more replies)
  1 sibling, 3 replies; 43+ messages in thread
From: Adam Beneschan @ 1994-10-01  1:24 UTC (permalink / raw)


Magnus.Kempe@di.epfl.ch (Magnus Kempe) writes:

> John is still not satisfied that Ada 9X has a clean, elegant solution
> to his problem (two abstractions, Employee and Office, have a recursive
> dependency--how to model/implement that?).

I'd like to chime in and say I'm not satisfied either.  First, the
disclaimers: I'm just starting to learn about OO concepts and Ada 9X,
so I may not know entirely what I'm talking about.  I certainly don't
yet understand the meaning of all the new 9X syntaxes presented in the
examples.  Certainly, some of my ideas are influenced by what little I
know about Eiffel.  Also, I'm by no means an expert at language
design.

However, the solution shown feels like a "workaround", not a clean,
elegant solution.  I guess it could be called a clean, elegant
workaround.  My reasoning is as follows:

If the dependencies went only one way (an Employee contains a
reference to an Office, or vice versa), the implementation would be
straightforward.  Because there are mutual dependencies, though, a new
package Company has to be added.  This means that the package Company
is there "just to get the Ada language to do what you want", not to
represent any concept or abstraction that has anything to do with the
application.  The Company package doesn't add anything to another
programmer's understanding of how the program works--and it may in
fact be a hindrance, since it has a name that could mislead one into
thinking that it actually has some relevance.  Perhaps it would be
better to name it Aybnuiyuiwrfnkb or something.

Similarly, the example uses inheritance (type Employee is new
Employee_Parent is inheritance, right?).  As I understand it,
inheritance's primary purpose is to model "IS-A" or "IS-A-KIND-OF"
relationships between object types (I believe it's sometimes used
purely for code reuse, although that seems like a misuse of the
concept to me).  Here, inheritance doesn't really model anything--it's
just something that got set up in order to get things to work.  Like
the Company package, the use of inheritance here doesn't add anything
to one's understanding of the program.

I assume that the examples given work correctly.  I assume also that
they satisfy the really important requirement--namely, that packages
other than Employee, including Office, have access to everything the
Employee package wishes to put in its interface and to nothing else
having to do with Employees (and similarly for Offices).  Thus, this
is certainly an acceptable workaround for the problem.  But "clean"
and "elegant"?  Maybe I'm being too much of a purist, but I have
trouble characterizing something that requires adding this much code
that has no meaning to someone reading the program, as elegant.  It
seems more like a flaw in 9X that things have to be done this way.
And based on my experience writing large Ada 83 programs, my gut
instinct is that this is the sort of flaw that will be cursed by many
9X users down the line.  I also realize that if we tried to make Ada
9X absolutely perfect, it wouldn't be Ada 9X any more but something
like Ada 23.  :-)

What I think John wants (and I concur) is some way for package
specifications to use accesses to types in other packages that haven't
been compiled yet--sort of like a forward declaration that crosses
package boundaries.  Perhaps something like this could be added to 
Ada 0X or whatever the next revision of the language would be.

    with ...;         -- other stuff
    without Office;   -- "without" means that we're referring to a
                      -- package that hasn't been compiled yet 
    package Employee is
       ...
    private
       type Object is tagged record
          ... -- various components involving that "other stuff"
          Its_Occupied_Office : Office.Pointer;
              -- here we're promising that when Office is compiled,
              -- there will be an ACCESS type named Pointer.  This
              -- will be verified at link time.  
       end record;
    end Employee;

Oh well, just my 2 cents . . .

                                -- Adam

(P.S. John, how about we get together and start our own church?  :-)


--
The above views are my own and do not necessarily represent those of
Irvine Compiler Corp.



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-30 20:27       ` Norman H. Cohen
@ 1994-10-01  1:47         ` John Volan
  1994-10-01 20:44           ` Tucker Taft
  1994-10-03 11:29           ` Robert I. Eachus
  0 siblings, 2 replies; 43+ messages in thread
From: John Volan @ 1994-10-01  1:47 UTC (permalink / raw)


ncohen@watson.ibm.com (Norman H. Cohen) writes:

>The five lines just above seem to contradict your earlier statement that
>the pointer types (the imnplementation of the binary association between
>offices and employees) should be private, so I'll assume you really meant
>what you said the first time and not the second time.  Pasting together
>bits and pieces of solutions proposed by Mark Biggar, Magnus Kempe, and
>me, isn't the following all that you're really looking for?

Well, I guess I haven't been very clear, sorry about that.  Maybe the post
I just sent out with all the pictures will help, but let me try again here.

Your solution is perfectly fine in terms of what I think you thought I 
wanted  :-).  I described the problem in terms of a single association and
two classes, and if that's all there were to it, capturing the mutual 
dependency as an abstraction works well.  But you see, I always had in mind
expanding this out to many classes with many interrelationships, and I don't
think your scheme will scale up well.

Suppose, instead of just writing package "Employees_And_Offices", we had to
write package "Company".  Suppose we tried to support not only Employees and
Offices, but also other things in the problem domain of Company management.
Things like Memos and Meetings and Job_Descriptions and Inventories and Stock
and Benefits and so on and on and on ...

(Please bear with me, I'm not even going to try to make this a realistic
example, nor do I propose that you would really want to cover this particular
problem domain this way.  But for the sake of argument, here goes.)

   with Sets;
   package Company is

      type Employee_Parent is abstract tagged private;
      type Employee_Pointer is access Employee_Parent'Class;
      package Employee_Sets is new Sets (Employee_Pointer);

      type Office_Parent is abstract tagged private;
      type Office_Pointer is access Office_Parent'Class;
      package Office_Sets is new Sets (Office_Pointer);

      type Memo_Parent is abstract tagged private;
      type Memo_Pointer is access Memo_Parent'Class;
      package Memo_Sets is new Sets (Memo_Pointer):

      type Meeting_Parent is abstract tagged private;
      type Meeting_Pointer is access Meeting_Parent'Class;
      package Meeting_Sets is new Sets (Meeting_Pointer);

      type Job_Description_Parent is abstract tagged private;
      type Job_Description_Pointer is access Job_Description_Parent'Class;
      package Job_Description_Sets is new Sets (Job_Description_Pointer);

      type Inventory_Parent is abstract tagged private;
      type Inventory_Pointer is access Inventory_Parent'Class;
      package Inventory_Sets is new Sets (Inventory_Pointer);

      ... ad nauseum

      -- Nondispatching  operations concerned with the relationships
      --    between offices and employees,
      --    between memos and employees,
      --    between memos and meetings,
      --    between meetings and employees,
      --    between employees and job descriptions,
      --    between employees and inventories,
      --    between inventories and stock,
      --    between employees and benefits,
      --    ... ad nauseum

   private

      type Employee_Parent is tagged
         record
            Its_Job_Description      : Job_Description_Pointer;
            Its_Occupied_Office      : Office_Pointer;
            Its_Sent_Memos           : Memo_Sets.Set;
            Its_Received_Memos       : Memo_Sets.Set;
            Its_Attended_Meetings    : Meeting_Sets.Set;
            Its_Coordinated_Meetings : Meeting_Sets.Set;
            ...
         end record;

      type Office_Parent is tagged
         record
            Its_Occupying_Employee: Employee_Pointer;
            Its_Employee_Occupancy_Memos : Memo_Sets.Set;
            ...
         end record;

      type Memo_Parent is tagged
         record
           Its_Sending_Employee    : Employee_Pointer;
           Its_Receiving_Employees : Employee_Sets.Set;
           ...
         end record;
      
      type Meeting_Parent is tagged
         record
           Its_Announcement_Memo     : Memo_Pointer;
           Its_Followup_Memo         : Memo_Pointer;
           Its_Atteding_Employees    : Employee_Sets.Set;
           Its_Coordinating_Employee : Employee_Pointer;
           ...
         end record;

      type Job_Description is tagged
         record
           Its_Described_Employees : Employee_Sets.Set;
         end record;

      . . . ad nauseum

   end Company;

You see, no matter how nicely you abstract the coupling between Employee and
Office, no matter how well you hide the implementation of the links, no matter
how well you defer the "other stuff" to the child packages, the fact remains 
that you are declaring the Employee and Office types in the same declarative
region.  If a mutually-recursive association forces you to declare two class
types (however abstract) in the same declarative region, then the effect is 
going to be transitive, and it essentially can force you to put the entire
semantic network of a large problem domain into one monolithic package.
Adding just one more class or association into the network, because of a
requirements change, will force you to recompile this package, all its children,
all their clients ... possibly the whole application.

On the other hand, if we can manage to maintain 1 Package = 1 Class even 
despite mutual recursion, then we at least have some hope of managing a large, 
complex problem -- one class at a time.  Isn't that what object-orientation is 
all about?

I think, thanks to Tucker and all the rest of you, that I managed to find an
Ada9X solution to this problem, a couple of posts back.  (If that scheme doesn't
work, please let some kind person tell me and put me out of my misery. :-)
Perhaps this mutual recursion business is a minor issue, perhaps not, but at
least I'm comforted that Ada9X could deal with it if it had to.


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

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-30 22:46       ` Matt Kennel
@ 1994-10-01  2:11         ` John Volan
  0 siblings, 0 replies; 43+ messages in thread
From: John Volan @ 1994-10-01  2:11 UTC (permalink / raw)


mbk@inls1.ucsd.edu (Matt Kennel) writes:

>How about this kind solution?  I'm going to have to write in ersatz
>Eiffel/Sather:


Ah, you beat me to it, Matt!  Or did my post get out first?  Anyway...


>class TWO_WAY_RELATION{T1,T2} is
>      private map_1_to_2:MAP{T1,T2}; -- MAP{T1,T2} is a hash table
>			             -- data structure that finds T2's given
>				     -- T1's as keys.
>      private map_2_to_1:MAP{T2,T1};
>
>      create is
>	map_1_to_2 := #MAP{T1,T2};
>	map_2_to_1 := #MAP{T1,T2}; end;
>
>      add_pair(x:T1,y:T2) is
>	 map_1_to_2.insert(x,y);
>	 map_2_to_1.insert(y,x);
>      end;
>
>      find_one_given_two(y:T2):T1 is
>	return map_2_to_1.find(y); end;
>
>      find_one_given_two(x:T1):T2 is
>	return map_1_to_2.find(x); end;
>
>end; -- class TWO_WAY_RELATION


Absolutely right.  Ada can certainly do that too, with generic packages.


>class EMPLOYEE is
>	private shared mapper:TWO_WAY_RELATION{EMPLOYEE,OFFICE};
>		-- shared so all instances access the same object
>
>	find_my_office : OFFICE is
                         ^^^^^^

Ah, but you see, this is precisely my point.  You are in the midst of writing
the interface (and implementation -- Eiffel does it all at once) of class
EMPLOYEE.  Class OFFICE has not yet been written!  I'll assume that this truly 
works in Eiffel and that these three classes are three separate compilation
units, not just one file.  If so, then Eiffel is able to manage the 
Identity/Interface/Implementation Trichotomy I talked about a couple posts ago.

 
>		mapper.find_two_given_one(self); end;
>
>	set_my_office(o:OFFICE) is
>		mapper.add_pair(self,o); end;
>
>end; -- EMPLOYEE
>
>class OFFICE is
>	private shared mapper:TWO_WAY_RELATION{EMPLOYEE,OFFICE}
>		-- shared so all instances access the same object
>
>	find_my_employee : OFFICE is
>		mapper.find_one_given_two(self); end;
>
>	set_my_employee(e:EMPLOYEE) is
>		mapper.add_pair(e,self); end;
>end; -- OFFICE
>
>---
>
>Now, both Office and Employee present interfaces that can find
>and set each one's counterpart.  Neither knows how to do this
>except through the 'mapper' object, which presents a clean interface
>for managing pairs.
>
>Comments?


If this works, it's not because the mapper object did it for them.  It's because
Eiffel can deal with two classes that are mutually dependent even in their
interfaces!  (Matt, can you or some other Eiffel person confirm this?)

Well, Ada folks, if this works ... need I say more?


>--
>-Matt Kennel  		mbk@inls1.ucsd.edu
>-Institute for Nonlinear Science, University of California, San Diego
>-*** AD: Archive for nonlinear dynamics papers & programs: FTP to
>-***     lyapunov.ucsd.edu, username "anonymous".

--John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
@ 1994-10-01 12:01         ` Magnus Kempe
  1994-10-01 18:43         ` Mark A Biggar
  1994-10-02 16:41         ` John Volan
  2 siblings, 0 replies; 43+ messages in thread
From: Magnus Kempe @ 1994-10-01 12:01 UTC (permalink / raw)


adam@irvine.com (Adam Beneschan) writes:
: ...  the package Company is there "just to get the Ada language to do
: what you want", not to represent any concept or abstraction that has
: anything to do with the application.

This is incorrect.  The requirement is for two ADTs which are mutually
dependent.  Ada 9X offers a hierarchical namespace which allows us to
capture the fact that Employee and Office belong together (you're not
going to use one without the other), and the root package Company _holds_
that mutual dependency.

: As I understand it, inheritance's primary purpose is to model "IS-A"
: or "IS-A-KIND-OF" relationships between object types (I believe it's
: sometimes used purely for code reuse, although that seems like a
: misuse of the concept to me).  Here, inheritance doesn't really model
: anything--it's just something that got set up in order to get things
: to work.

You are right that code usually doesn't "model" anything.  It's just
there to _implement_ a model.  You seem to hold that inheritance is
good only when it corresponds exactly to an "is-a" relationship of
your model.

But there are two kinds of inheritance: interface and implementation (or
code sharing).  The first corresponds to is-A relationships; the second
is a means for code sharing.  In the particular example you cite, the
derivation was not done in the interface but in the private part, so as
to _hide_ the inheritance.


Compare undue interface inheritance (the client sees the ADT and
implementation details, as well as some List stuff which has nothing
to do with a Stack ADT)

with Lists;
package Stacks is
  type Stack_Type is
    new Lists.List_Type -- !!! Stack is-a List
    with private;
  ... -- Push, Pop, Size
  
  ... -- somehow try to hide the operations of List_Type which
      -- violate the Stack abstraction
private
  type Stack_Type is
    new List_Type
    with null record;
end Stacks;


vs. implementation inheritance (the client does not see anything
except the proper ADT)

with Lists;
package Stacks is
  type Stack_Type is
    private;
  ... -- Push, Pop, Size
private
  type Stack_Type is
    new Lists.List_Type -- !!! Stack is-implemented-by-a List
    with null record;
end Stacks;


There is nothing revolutionary here, and nothing peculiar to Ada.  The
distinction between interface and implementation inheritance is quite
common and useful.  One may have a dislike for implementation inheritance,
but that's no sufficient reason to forbid its use to those who do find
it advantageous.  Is it an ugly "workaround"?  Maybe, to the extent that
someone somewhere will castigate any high-level language mechanism for
being a workaround of their favorite assembly language feature.

-- 
Magnus Kempe		"I know not what course others may take, but as for me,
Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
  1994-10-01 12:01         ` Magnus Kempe
@ 1994-10-01 18:43         ` Mark A Biggar
  1994-10-02 16:41         ` John Volan
  2 siblings, 0 replies; 43+ messages in thread
From: Mark A Biggar @ 1994-10-01 18:43 UTC (permalink / raw)


In article <Cwyzx4.H3K@irvine.com> adam@irvine.com (Adam Beneschan) writes:
>I assume that the examples given work correctly.  I assume also that
>they satisfy the really important requirement--namely, that packages
>other than Employee, including Office, have access to everything the
>Employee package wishes to put in its interface and to nothing else
>having to do with Employees (and similarly for Offices).  Thus, this
>is certainly an acceptable workaround for the problem.  But "clean"
>and "elegant"?  Maybe I'm being too much of a purist, but I have
>trouble characterizing something that requires adding this much code
>that has no meaning to someone reading the program, as elegant.  It
>seems more like a flaw in 9X that things have to be done this way.
>And based on my experience writing large Ada 83 programs, my gut
>instinct is that this is the sort of flaw that will be cursed by many
>9X users down the line.  I also realize that if we tried to make Ada
>9X absolutely perfect, it wouldn't be Ada 9X any more but something
>like Ada 23.  :-)

It is my understanding that a very early decision in the ada9X revision
process was to only add sufficiant new features to Ada to allow development
of OOP system using a building block approach.  In other words there should
be no "One True Way" to OOP in Ada9x.  Some of the early ideas proposed
for adding OOP to Ada9x would have forced a "One True Way" on the language
(E.G., adding package types).  Because Ada9x was designed using this
building block approach, some things might not end up looking as "Elegant"
of "Clean" as in other languages that do have a "One True Way", but the added
flexability should make designing OO system that use only as much of the OO
stuff as needed very simple compared to "One True Way" system that require
you to used everything whether you want it or not.  I personally thing
that the Ada9x design team has done an excellent job of sticking to the 
building block approach and have avoided a "One True Way" while providing
all the features needed to design OOP systems.

--
Mark Biggar
mab@wdl.loral.com




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-01  1:47         ` John Volan
@ 1994-10-01 20:44           ` Tucker Taft
  1994-10-03 11:29           ` Robert I. Eachus
  1 sibling, 0 replies; 43+ messages in thread
From: Tucker Taft @ 1994-10-01 20:44 UTC (permalink / raw)


I suggest that future follow-ups of this note restrict
themselves to the comp.lang.ada newsgroup.  This seems to be
pretty much exclusively a discussion of how to do things
in Ada 9X now, rather than a general discussion about OOP.

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-30 22:13           ` John Volan
@ 1994-10-02  3:31             ` Andrew Lees
  0 siblings, 0 replies; 43+ messages in thread
From: Andrew Lees @ 1994-10-02  3:31 UTC (permalink / raw)


This discussion has led me to wonder if the source of the problem is
a clash between the desire to have "one package = one type" and the
essential structure of the problem being discussed.  This may appear
a little meta-physical, but I wonder if the relationship between
employee and office is not _core_ to the definition of employee, and
similarly for office (at least in the context of this problem ).
If this is the case, then
having the definitions of office and employee separate is itself
part of the problem - at an essential level they are not separate.

A possible solution recognising the intertwined nature of the two types
may be to have the office and employee identifiers (= access types)
defined in a single package as private types with their structure
left to the body.  This common package has nothing else, or may have
common employee-office operations.  The separate employee and office
operations are then defined in child packages, which can re-export the
associated indentifier type for those clients who are interested solely
in "employee" or "office" things, and do not wish to concern themslves
about the combined aspects.

I'm not sure if this is directly related to what was intended, but there
are certainly cases where this type of solution is required.

Andy Lees.



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
  1994-10-01 12:01         ` Magnus Kempe
  1994-10-01 18:43         ` Mark A Biggar
@ 1994-10-02 16:41         ` John Volan
  1994-10-02 23:33           ` Matt Kennel
                             ` (3 more replies)
  2 siblings, 4 replies; 43+ messages in thread
From: John Volan @ 1994-10-02 16:41 UTC (permalink / raw)



adam@irvine.com (Adam Beneschan) writes:

>I'd like to chime in and say I'm not satisfied either.... 

I fear there may be many of us out there.

[snip]

>And based on my experience writing large Ada 83 programs, my gut
>instinct is that this is the sort of flaw that will be cursed by many
>9X users down the line...

That's my fear as well.  That's why I've been trying very hard to find
an Ada9X solution.  Eiffel can do decoupled mutual recursion standing on its
head.  Smalltalk virtually swims in a sea of decoupled mutual recursion 
(trivial in a typeless language).  I suspect C++ can actually manage it as well.
(Can any C++ folks confirm this?  Realize what I mean: Can you forward-declare 
a class and then *not* complete its declaration in the same header file, and 
yet complete the declaration of a client class?  Or do you *have* to declare 
both classes in the same header file?)

We want Ada9X to be successful.  We want Ada9X to attract programmers who
might not have considered using Ada before.  If something is trivial in other
languages, it is not necessary that it be *trivial* in Ada9X.  But it must
at least be *feasible*.

[snip]

>What I think John wants (and I concur) is some way for package
>specifications to use accesses to types in other packages that haven't
>been compiled yet--sort of like a forward declaration that crosses
>package boundaries.  Perhaps something like this could be added to 
>Ada 0X or whatever the next revision of the language would be.

I agree wholeheartedly with the *goal*, but not with the *means* you suggest.

Goal: Some kind of "forward declaration" that crosses package boundaries.
(Good and worthy goal.)

Means: Some way for package specifications to use access types in other
packages that *haven't been compiled yet*.  (Horrible means, terrible 
implications, shudder, shudder :-)

Actually, I have a better idea.  Let's take a bit of inspiration from a
notion that is already intrinsic to the design of Ada:  Although a package is
considered to be a single, self-contained, organic entity -- a *unit* of program
modularity -- nevertheless it is syntactically broken up into two *parts* that
present complementary *views* of that single unit -- two views which can be
*separately compiled*. They are: (1) the package specification, which presents
the *interface* view of the package, and (2) the package body, which presents
the *implementation* view of the package.

All that is really needed is to a break off a third syntactic part that can
offer a view of the package where we can make "forward declarations"  -- or
rather, where "incomplete" declarations could be tolerated, because they
will eventually be completed in the package specification.  We could offer up
this new part as a separately-compilable piece of code.  In keeping with the
long-standing Ada practice of *recycling* reserved words for new uses rather 
than creating new ones (bet you didn't know that Ada language designers were 
environmentalists! :-), let's call this new package part a "package abstract"
(where "abstract" is a noun rather than an adjective, like the "abstract"
of an article).  

Rules:

(1) A package abstract would be considered a declarative region contiguous with
    the public part of the corresponding package specification.  The spec
    would implicitly have visibility to any declaration that appeared in its
    corresponding package abstract.

(2) To avoid a situation analogous to the problem of "optional" package bodies
    in Ada 83, we would need to syntactically distinguish ordinary package 
    specs from those that have an abstract. The following would be illegal:

    (a) Compiling an ordinary spec when a package abstract with the same name
        is already in the library.

    (b) Compiling a package abstract when an ordinary package spec with the same
        name is already in the library.

    (c) Compiling a package-spec-with-abstract before its corresponding 
        package abstract.  (Analogous to compiling a package body before
        the corresponding package spec.)

    (Of course, for the GNAT compiler, you'd have to translate these into 
    source-file dependency rules rather than order-of-compilation rules.)

(3) We would also need an alternate syntax for the "with" clause, to allow a
    client to import only the abstract of a package. Such a client would only 
    be able to make use of those declarations from the package abstract that
    were actually "complete".  Later (in its body, say) a client could import
    the full spec via a normal "with" clause, and this would give it access
    to the complete view of all the public features of the package.

Example:

	----------------------------------------------------------------------

	abstract of package Employee is -- note additional syntax

	  type Object is tagged limited private;
	    -- This is an example of an incomplete declaration that 
            -- can be "tolerated" in a package abstract

          type Pointer is access all Object'Class;
	  None : constant Pointer := null;
            -- These are examples of complete declarations (or are they?)

	end Employee;

	----------------------------------------------------------------------

	with abstract of Office; -- examples of import of package abstracts
	with abstract of Memo;
	with abstract of Meeting;
	with abstract of Job_Description;
	...

	package with abstract Employee is -- note additional syntax

	  ...

	  function Office_Occupied_By (The_Employee : in Employee.Object'Class)
	    return Office.Pointer;

	  procedure Occupy_Office (The_Employee : in out Employee.Object'Class;
	                           New_Office   : in     Office.Pointer);
	  -- mutually recursive with Office.Accomodate_Employee

	  ...

	private
	  type Object is tagged limited
	    record
	      ... 
	      Its_Occupied_Office : Office.Pointer;
	      Its_Job_Description : Job_Description.Pointer;
	      ...
	      ... 
	    end record;
	end Employee;

	----------------------------------------------------------------------

	with Office; -- normal import of full package specifications
	with Memo;
	with Meeting;
	with Job_Description;
	...

	package body Employee is

	  ...

	  function Office_Occupied_By (The_Employee : in Employee.Object'Class)
	    return Office.Pointer is
	  begin
	    return The_Empoyee.Its_Occupied_Office;
	  end Office_Occupied_By;

	  procedure Occupy_Office (The_Employee : in out Employee.Object'Class;
				   New_Office   : in     Office.Pointer) is
	    use type Office.Pointer;
	  begin
	    if New_Office /= Office.None and then
	       New_Office /= The_Employee.Its_Occupied_Office then
	    begin
	      Employee.Vacate_Office (The_Employee);
	      The_Employee.Its_Occupied_Office := New_Office;
	      Office.Accomodate_Employee (The_Office.all, The_Employee'Access);
	    end if;
	  end Occupy_Office;
	
	  ...

	end Employee;

	----------------------------------------------------------------------

Obvious Problems:

(1) It's certainly pretty late in the game to consider this kind of thing
    for Ada9X.  Maybe Ada0X could do something with it.  Also, I haven't
    studied the whole MRT history, so I don't know if someone has already
    thought of something like this.  I don't want to take credit (or blame :-)
    for someone else's idea.

(2) In making this suggestion, I am engaging in the execrable practice of
    "YALFing" (where YALF = "Yet Another Language Feature" :-).  This extra 
    feature might make it trivial to write code in this particular style, 
    but is it worth the added complexity to the language?  (I'm still holding 
    out hope that there is some elegant way of combining the *existing* features
    of Ada 9X to support this coding style.)

(3) Is a pointer type really "complete" by the end of a package abstract?
    What if, instead of a private type, the Object type in the package abstract
    were an incomplete type declaration that got completed in the *public*
    part of the package spec?  What if that complete type were a record type
    or an array type?  Would a client-of-spec be able to use record-component
    or array-indexing syntax with the pointer type, whereas a client-of-abstract
    would not?  

(4) Could a package-with-abstract be generic?  Where would the generic clause
    go?  On the abstract?  On the spec?  On both?  If both, would they have to
    be identical, or could the abstract introduce some generic formal parameters
    and the spec introduce more?  How would instantiation work?  Would you need
    an alternate syntax for instantiation-of-abstract vs. instantiation-of-spec?
    What if a client instantiated the abstract and then later instantiated the
    spec with the same name and actual parameters?  Would they be two views of 
    the same instantiated package, or two different instantiated packages?  
    What if you tried this at the library level?  What implications does all 
    of this have for the generic contract model?

Not being a language lawyer, I'm sure there must be other conceptual
difficulties and complexities.  I can't begin to assess what it would cost
you compiler-writers out there to try to implement this feature, but
I can well imagine it giving you the screaming willies^H^H^H^H^H hillaries! ;-)

-- John Volan

>                                -- Adam

>(P.S. John, how about we get together and start our own church?  :-)

Dibbs!  I get to be pope first! :-)

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-02 16:41         ` John Volan
@ 1994-10-02 23:33           ` Matt Kennel
  1994-10-03  8:07           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 43+ messages in thread
From: Matt Kennel @ 1994-10-02 23:33 UTC (permalink / raw)


John Volan (jgv@swl.msd.ray.com) wrote:

: That's my fear as well.  That's why I've been trying very hard to find
: an Ada9X solution.  Eiffel can do decoupled mutual recursion standing on its
: head.  Smalltalk virtually swims in a sea of decoupled mutual recursion 
: (trivial in a typeless language). 
: I agree wholeheartedly with the *goal*, but not with the *means* you suggest.


: Goal: Some kind of "forward declaration" that crosses package boundaries.
: (Good and worthy goal.)

: Means: Some way for package specifications to use access types in other
: packages that *haven't been compiled yet*.  (Horrible means, terrible 
: implications, shudder, shudder :-)

: Actually, I have a better idea.  Let's take a bit of inspiration from a
: notion that is already intrinsic to the design of Ada:  Although a package is
: considered to be a single, self-contained, organic entity -- a *unit* of program
: modularity -- nevertheless it is syntactically broken up into two *parts* that
: present complementary *views* of that single unit -- two views which can be
: *separately compiled*. They are: (1) the package specification, which presents
: the *interface* view of the package, and (2) the package body, which presents
: the *implementation* view of the package.

: All that is really needed is to a break off a third syntactic part that can
: offer a view of the package where we can make "forward declarations"  -- or
: rather, where "incomplete" declarations could be tolerated, because they
: will eventually be completed in the package specification.

Eeek.  Sorry I disagree here.  Remember, the road to C++ was paved with good
intentions.  

Instead of adding complexity, subtract it.  Declare _ex cathedra_
"recursive definitions are allowed".  Let the compiler implement.

--
-Matt Kennel  		mbk@inls1.ucsd.edu
-Institute for Nonlinear Science, University of California, San Diego
-*** AD: Archive for nonlinear dynamics papers & programs: FTP to
-***     lyapunov.ucsd.edu, username "anonymous".



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-09-29  1:46   ` John Volan
  1994-09-29 13:57     ` Tucker Taft
  1994-09-29 18:10     ` R. William Beckwith
@ 1994-10-03  0:33     ` Cyrille Comar
  2 siblings, 0 replies; 43+ messages in thread
From: Cyrille Comar @ 1994-10-03  0:33 UTC (permalink / raw)


jgv@swl.msd.ray.com (John Volan) writes:

: can the client do with that pointer?  As far as it can see, the Office object
: being pointed to is totally featureless.  Oh, sure, it's possible that this
:Office object is really some subclass of Abstract_Office that does have useful
: features, but this information isn't available to the client.The only way the
: client could get to that information would be to use an unchecked conversion 
: to change this "opaque" pointer into a pointer to the "non-opaque" Office 
: subclass below.  (I think folks in the other languages would call this 
: "downcasting".) But this practice breaks type-safety!

As far as  I know, downcasting is allowed in Ada9x and is type-safe (a check is
performed at run-time) by using a regular conversion not an
unchecked-conversion, so Mark's solution seems very close to what you need.
-- 
------------------------------------------------------------------------
Cyrille Comar,                                  E-mail: comar@cs.nyu.edu
Gnat Project                                    US phone: (212) 998-3489




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.?
  1994-10-02 16:41         ` John Volan
  1994-10-02 23:33           ` Matt Kennel
@ 1994-10-03  8:07           ` Magnus Kempe
  1994-10-03 12:14           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Robert I. Eachus
  1994-10-03 20:29           ` Harry Koehnemann
  3 siblings, 0 replies; 43+ messages in thread
From: Magnus Kempe @ 1994-10-03  8:07 UTC (permalink / raw)


jgv@swl.msd.ray.com (John Volan) writes:
: 
: [summary--packages should be broken up in three, not two, parts:
:  specification, body, and "abstract".]

John,

I suggest you stop for a while, and take the time to examine the
solutions which have been offered (and read the RM 9X, clause 10.1,
on program structure/separate compilation).

Child units are _essentially_ a means to break packages into parts.  This
is something _no_ other language offers, so it is quite understandable that
many will not always immediately see how useful it is (and some are likely
to abuse it, as I tend to :-).

The final solution whch Norm presented solves _perfectly_ the problem you
submitted, with abstract types, class-wide subprograms, and a hierarchy of
packages.

Of course, Ada is not Smalltalk.  But then, Smalltalk is not Ada either.
In other words: Don't try to blindly transfer programming patterns from one
language to another.

-- 
Magnus Kempe		"I know not what course others may take, but as for me,
Magnus.Kempe@di.epfl.ch  Give me Liberty... or give me Death!" -- Patrick Henry



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-01  1:47         ` John Volan
  1994-10-01 20:44           ` Tucker Taft
@ 1994-10-03 11:29           ` Robert I. Eachus
  1 sibling, 0 replies; 43+ messages in thread
From: Robert I. Eachus @ 1994-10-03 11:29 UTC (permalink / raw)


In article <1994Oct1.014758.10687@swlvx2.msd.ray.com> jgv@swl.msd.ray.com (John Volan) writes:

  > ... Suppose, instead of just writing package
  > "Employees_And_Offices", we had to write package "Company".
  > Suppose we tried to support not only Employees and Offices, but
  > also other things in the problem domain of Company management.
  > Things like Memos and Meetings and Job_Descriptions and
  > Inventories and Stock and Benefits and so on and on and on ...

   Hmmm... I can have a model of a company MIS environment where there
is one package of high-level abstractions such as people, places, and
things.  I can also have lower-level abstractions, which can be a
hierarchy of child packages, since I want to have this as a sub
system.  Seems like a reasonable approach, we just have to rename some
of the types in the examples:

    type People is abstract tagged private;
    type People_Pointer is access People'Class;
    ...etc.

    Now everything at the top level seems reasonable, and the lower
down decisions, such as whether consultants can be assigned offices,
is implementable at the appropriate level.  Adding attributes to the
top level abstraction of people will require modifications to the root
package, for example adding a fax number field, or a teleport address.

    I assume that such modifications occur at a relatively slow pace,
but notice that recently we have had at least TWO additions to
business cards--fax number and e-mail address.  The previous sucn
change was the addition of telephone numbers (or perhaps area codes
for telephone numbers).  But that started happening about a century
ago.  (Area codes started appearing in the late fifties.)  The next
addition will probably be photographs--the MITRE on-line directory has
them now.

  > On the other hand, if we can manage to maintain 1 Package = 1
  > Class even despite mutual recursion, then we at least have some
  > hope of managing a large, complex problem -- one class at a time.
  > Isn't that what object-orientation is all about?

    Here I have to disagree, but it may be an issue of terminology.
In Smalltalk there is ONE superclass.  Do you hear complaints about
Smalltalk which state that this prevents encapsulation?  No.

    As I see it you are really arguing that superclasses shouldn't
have to "know" about subclass specific interfaces and details.  Stated
that way, I don't have a problem with your assertion, or with Ada 9X.

    However, identifying the appropriate level in the tree for a
specific interface is always going to be a hard problem.  And we are
going to spend a lot of time fixing wrong guesses.  That is a part of
the design (and programming) process, and probably can never be
eliminated entirely.

--

					Robert I. Eachus

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-02 16:41         ` John Volan
  1994-10-02 23:33           ` Matt Kennel
  1994-10-03  8:07           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
@ 1994-10-03 12:14           ` Robert I. Eachus
  1994-10-04  2:12             ` R. William Beckwith
  1994-10-04 16:00             ` John Volan
  1994-10-03 20:29           ` Harry Koehnemann
  3 siblings, 2 replies; 43+ messages in thread
From: Robert I. Eachus @ 1994-10-03 12:14 UTC (permalink / raw)


In article <1994Oct2.164105.13127@swlvx2.msd.ray.com> jgv@swl.msd.ray.com (John Volan) writes:

   > adam@irvine.com (Adam Beneschan) writes:

   >> I'd like to chime in and say I'm not satisfied either.... 

   > I fear there may be many of us out there.

   > That's my fear as well.  That's why I've been trying very hard to
   > find an Ada9X solution.  Eiffel can do decoupled mutual recursion
   > standing on its head.  Smalltalk virtually swims in a sea of
   > decoupled mutual recursion (trivial in a typeless language).  I
   > suspect C++ can actually manage it as well.  (Can any C++ folks
   > confirm this?  Realize what I mean: Can you forward-declare a
   > class and then *not* complete its declaration in the same header
   > file, and yet complete the declaration of a client class?  Or do
   > you *have* to declare both classes in the same header file?)

   Stated this way, you are trying to solve a different problem than
the original one.  This one has a very simple solution: child units.
This eliminates the "necessity" for mutual recursion in the visible
package specifications.

   However, there are limitations to this approach.  (Let me do a bit
of a sketch here:

   package Employees is
     type Employee is tagged private;
     ...
   end Employees;

   package Offices is
     type Office is tagged private;
     ...
   end Offices;

   with Offices; use Offices;
   function Employees.Assigned_Office(E: in Employee) return Office;

   with Employees; use Employees;
   function Offices.Occupant(O: in Office) return Employee;

   As sketched, this provides a clean, clear interface.  However there
are two problems.  First, there is going to have to be some
abstraction breaking to implement the bodies of these units.  It can
be done in a type safe manner, with private child units, and there are
other possibilities.

   Second, these functions are not inheritable, so you might want:

   with Offices; use Offices;
   function Employees.Assigned_Office(E: in Employee'CLASS)
        return Office'CLASS;

   with Employees; use Employees;
   function Offices.Occupant(O: in Office'CLASS) return Employee'CLASS;

   instead of the versions above.  Since such subprograms would have
to deal with all of the (possibly) many subclasses to employees or
offices, this approach can result in unmaintainable code. However for
small projects, or for attributes which are orthogonal to the rest of
the abstraction, this approach works just fine.

    If any extension to the current draft standard is required in this
area--and I don't think one is--it would be to change 3.2.3(6) to make
child units eligible as primitive operations, probably under the
influence of a pragma.   I certainly don't recommend such a change to
the standard--it would open up all sorts of holes--but that should
prevent someone from experimenting with such a pragma.

--

					Robert I. Eachus

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-02 16:41         ` John Volan
                             ` (2 preceding siblings ...)
  1994-10-03 12:14           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Robert I. Eachus
@ 1994-10-03 20:29           ` Harry Koehnemann
  3 siblings, 0 replies; 43+ messages in thread
From: Harry Koehnemann @ 1994-10-03 20:29 UTC (permalink / raw)


In article <1994Oct2.164105.13127@swlvx2.msd.ray.com> jgv@swl.msd.ray.com (John Volan) writes:
>
>That's my fear as well.  That's why I've been trying very hard to find
>an Ada9X solution.  Eiffel can do decoupled mutual recursion standing on its
>head.  Smalltalk virtually swims in a sea of decoupled mutual recursion 
>(trivial in a typeless language).  I suspect C++ can actually manage it as well.
>(Can any C++ folks confirm this?  Realize what I mean: Can you forward-declare 
>a class and then *not* complete its declaration in the same header file, and 
>yet complete the declaration of a client class?  Or do you *have* to declare 
>both classes in the same header file?)

Yes.  See the example.

	class A;
	Class B {
	    A *a;		// legal
	    ...
	    a -> print();	// illegal
	};

However, the body of B cannot perform any operations on 'a' since they
are not visible.  I think this would also work in Ada9X, except that
multiple pointer declarations would be incompatible with one another
due to Ada's typing mechanism.

From what I remember of Eiffel and Smalltalk (it's been a while), one
can invoke an operation from a class that has not yet been declared.
However, both C++ and Ada9X require a function (or operation) be
declared before it be used.  I think this is the feature you seek,
which is more than simply a forward declaration.

Hope this helps.
--
Harry Koehnemann			Arizona State University
hek@asu.edu				Computer Science Department



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-03 12:14           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Robert I. Eachus
@ 1994-10-04  2:12             ` R. William Beckwith
  1994-10-04 16:00             ` John Volan
  1 sibling, 0 replies; 43+ messages in thread
From: R. William Beckwith @ 1994-10-04  2:12 UTC (permalink / raw)


O.K., I guess I've been watching on the side lines too long :-).

I addressed the issue of mutual recursion (I called it the `withing'
problem) with Tuck early in our efforts to map CORBA IDL to Ada 9X.

Since then, we have solved the problem.  I have said to several people
that you may want to use a CORBA IDL to Ada translator even for
non-distributed systems.  Now is my chance to put up or shut up.

In C++ you can have mutual recursion:

    // file chicken.h

    #ifndef CHICKEN
    #define CHICKEN

    class Chicken;

    #include "egg.h"

    class Chicken {
	public:
	    Egg lay();
    };

    #endif


    // file egg.h

    #ifndef EGG
    #define EGG

    class Egg;

    #include "chicken.h"

    class Egg {
	public:
	    Chicken hatch();
    };

    #endif

In CORBA IDL you can have mutual recursion:

    // file chicken.idl

    #ifndef CHICKEN
    #define CHICKEN

    interface Chicken;

    #include <egg_file.idl>

    interface Chicken {
	Egg lay();
    };

    #endif


    // file egg.idl

    #ifndef EGG
    #define EGG

    interface Egg;

    #include <chicken_file.idl>

    interface Egg {
	Chicken hatch();
    };

    #endif

In Ada 9X you can have mutual recursion:

    (result of IDL to Ada 9X translator with simplifications)

    with Ada.Finalization;

    package Corba is

	type Object is tagged ...

	type Ref is tagged ...

    end Corba;

    -------------------------------------------------

    with Corba;

    package chicken_file is

	type Chicken_Ref is new Corba.Ref with null record;

    end chicken_file;

    -------------------------------------------------

    with Corba;
    with egg_file;

    package chicken_file.Chicken is

	type Ref is new Chicken_Ref with null record;

	function lay
		(Self : Ref)
	    return egg_file.Egg_Ref'Class;
	    -- returns a egg_file.Egg.Ref

    end chicken_file.Chicken;

    -------------------------------------------------

    with Corba;
    with egg_file.Egg;

    package chicken_file.Chicken.Impl is

	type Object is new Corba.Object with
	    record
		-- (implementation data)
	    end record;

	function lay
		(Self : in Object)
	    return Egg.Ref'Class;

    end chicken_file.Chicken.Impl;

    -------------------------------------------------

    with Corba;

    package egg_file is

	type Egg_Ref is new Corba.Ref with null record;

    end egg_file;

    -------------------------------------------------

    with Corba;
    with chicken_file;

    package egg_file.Egg is

	type Ref is new Egg_Ref with null record;

	function hatch
		(Self : Ref)
	    return chicken_file.Chicken_Ref'Class;
	    -- returns a chicken_file.Chicken.Ref

    end egg_file.Egg;

    -------------------------------------------------

    with Corba;
    with chicken_file.Chicken;

    package egg_file.Egg.Impl is

	type Object is new Corba.Object with
	    record
		-- (implementation data)
	    end record;

	function hatch
		(Self : in Object)
	    return Chicken.Ref'Class;

    end egg_file.Egg.Impl;

No with'ing problems here!

... Bill

-- 
e-mail: Bill.Beckwith@ois.com       |    Team Ada
Objective Interface Systems, Inc.   | dist, full O-O
1895 Preston White Drive, Suite 250 | multithreading
Reston, VA  22091-5448  U.S.A.      |    built in



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-03 12:14           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Robert I. Eachus
  1994-10-04  2:12             ` R. William Beckwith
@ 1994-10-04 16:00             ` John Volan
  1994-10-05 11:42               ` Robert I. Eachus
  1994-10-05 21:09               ` Matt Kennel
  1 sibling, 2 replies; 43+ messages in thread
From: John Volan @ 1994-10-04 16:00 UTC (permalink / raw)


eachus@spectre.mitre.org (Robert I. Eachus) writes:

>This one has a very simple solution: child units.
>This eliminates the "necessity" for mutual recursion in the visible
>package specifications.

>   However, there are limitations to this approach.  (Let me do a bit
>of a sketch here:

>   package Employees is
>     type Employee is tagged private;
>     ...
>   end Employees;

>   package Offices is
>     type Office is tagged private;
>     ...
>   end Offices;

>   with Offices; use Offices;
>   function Employees.Assigned_Office(E: in Employee) return Office;

>   with Employees; use Employees;
>   function Offices.Occupant(O: in Office) return Employee;

This approach is nice, as long as we are willing to give up having
these operations be primitives for their respective tagged types.
I guess the only way I'd expand on this would be to make these into
child packages rather than just child functions.

Also, it looks like this scheme gives up on the possibility of
incorporating the pointers directly into the tagged record types
themselves, since they would still need to be declared in the private
parts of the root packages somehow.  However, this scheme could be
combined very nicely with the approach Matt Kennel suggested of
off-loading the association itself to a third package that implemented
some kind of dual hash-table:

  ----------------------------------------------------------------------
  generic
    type Former (<>) is limited private;
    type Former_Pointer is access all Object'Class;
    type Latter (<>) is limited private;
    type Latter_Pointer is access all Object'Class;
  package One_To_One is

    function Latter_Of (The_Former : in Former_Pointer) return Latter_Pointer;
    function Former_Of (The_Latter : in Latter_Pointer) return Former_Pointer;

    procedure Associate
    ( The_Former : in Former_Pointer;
      The_Latter : in Latter_Pointer );

    procedure Dissociate
    ( The_Former : in Former_Pointer );

    procedure Dissociate
    ( The_Latter : in Latter_Pointer );

  end One_To_One;

  -- Implementation of this is left as an exercise for the reader ... :-)

  ----------------------------------------------------------------------

  with Employee;
  with Office;
  with One_To_One;
  package Employee_Occupies_Office is new 
    One_To_One (Former         => Employee.Object,
                Former_Pointer => Employee.Pointer,
                Latter         => Office.Object,
                Latter_Pointer => Office.Pointer);

  ----------------------------------------------------------------------

  with Office;
  with Employee_Occupies_Office;
  package Employee.Office_Occupied is

    -- Employee.Office_Occupied.That_Of
    function That_Of 
    ( The_Employee : in Employee.Object'Class ) return Office.Pointer;

    -- Employee.Office_Occupied.Associate 
    procedure Associate
    ( The_Employee : in out Employee.Object'Class;
      New_Office   : in     Office.Pointer );

    -- Employee.Office_Occupied.Dissociate
    procedure Dissociate
    ( The_Employee : in out Employee.Object'Class );

  end Employee.Office_Occupied;

  ----------------------------------------------------------------------

  package body Employee.Office_Occupied is

    -- Employee.Office_Occupied.That_Of
    function That_Of 
    ( The_Employee : in Employee.Object'Class ) return Office.Pointer is
    begin
      Employee_Occupies_Office.Latter_Of (The_Former => The_Employee'Access);
    end That_Of;

    -- Employee.Office_Occupied.Associate 
    procedure Associate
    ( The_Employee : in out Employee.Object'Class;
      New_Office   : in     Office.Pointer ) is
    begin
      Employee_Occupies_Office.Associate
      ( The_Former => The_Employee'Access,
        The_Latter => New_Office );
    end Associate;

    -- Employee.Office_Occupied.Dissociate
    procedure Dissociate
    ( The_Employee : in out Employee.Object'Class ) is
    begin
      Employee_Occupies_Office.Dissociate (The_Former => The_Employee'Access);
    end Dissociate;

  end Employee.Office_Occupied;

  ----------------------------------------------------------------------

  with Employee;
  with Employee_Occupies_Office;
  package Office.Employee_Occupant is

    -- Office.Employee_Occupant.That_Of
    function That_Of
    ( The_Office : in Office.Object'Class ) return Employee.Pointer;

     -- Office.Employee_Occupant.Associate
     procedure Associate
     ( The_Office   : in out Office.Object'Class;
       New_Employee : in     Employee.Pointer );

     -- Office.Employee_Occupant.Dissociate
     procedure Dissociate
     ( The_Employee : in out Employee.Object'Class );

  end Office.Employee_Occupant;

  ----------------------------------------------------------------------

  package body Office.Employee_Occupant is

    -- Office.Employee_Occupant.That_Of
    function That_Of 
    ( The_Office : in Office.Object'Class ) return Employee.Pointer is
    begin
      Employee_Occupies_Office.Former_Of (The_Latter => The_Office'Access);
    end That_Of;

    -- Office.Employee_Occupant.Associate 
    procedure Associate
    ( The_Office   : in out Office.Object'Class;
      New_Employee : in     Employee.Pointer ) is
    begin
      Employee_Occupies_Office.Associate
      ( The_Latter => The_Office'Access,
        The_Former => New_Employee );
    end Associate;

    -- Office.Employee_Occupant.Dissociate
    procedure Dissociate
    ( The_Office : in out Office.Object'Class ) is
    begin
      Employee_Occupies_Office.Dissociate (The_Latter => The_Office'Access);
    end Dissociate;

  end Office.Employee_Occupant;

  ----------------------------------------------------------------------

I guess the real philosophical issue that everything is hinging on
here is: "Is a binary association *part* of the abstraction of each of
the classes, or is it a *separate* abstraction of its own?"

Adam Beneschan objected to other solutions that put the association
structure in a common root package, on the grounds that nothing from
the "real world" was being modeled by that.  I disagree with that, and
agree with Magnus Kempe that in fact the association should be treated
as a first-class abstraction of its own, in some fashion.  However, I
do not agree with Magnus Kempe that those other solutions "perfectly"
implement this abstraction, because of the coupling that I see them
introducing.  If you want to avoid that coupling, then I think that
two child packages hanging off of the associated root class packages,
and/or a separate package entirely, would be better solutions.

But a corollary to this issue is the question: "Do association
operations *need* to be primitives of each of the associated classes?"
I'm beginning to think not, but I'm not sure I have a good handle on
this question yet.  Any thoughts?

>   As sketched, this provides a clean, clear interface.  However there
>are two problems.  First, there is going to have to be some
>abstraction breaking to implement the bodies of these units.  It can
>be done in a type safe manner, with private child units, and there are
>other possibilities.

I'm not sure what you mean by "abstraction breaking".  Is there some
trick we could use to get those pointers into the tagged records after all?
Perhaps "opaque" forms of the pointers that somehow get converted to
"transparent" forms later?

>   Second, these functions are not inheritable, so you might want:
>
>   with Offices; use Offices;
>   function Employees.Assigned_Office(E: in Employee'CLASS)
>        return Office'CLASS;
>
>   with Employees; use Employees;
>   function Offices.Occupant(O: in Office'CLASS) return Employee'CLASS;
>
>   instead of the versions above.  

Yes, I believe I drew the same conclusion early on, that these would
be class-wide, non-dispatching operations, even if they could somehow
be placed in the root package.  I think it's reasonable to assume that
a Rumbaugh-style association should apply equivalently to all
instances of a class and its subclasses

>Since such subprograms would have
>to deal with all of the (possibly) many subclasses to employees or
>offices, this approach can result in unmaintainable code. However for
>small projects, or for attributes which are orthogonal to the rest of
>the abstraction, this approach works just fine.

I don't think there's any problem here. It seems to me the most
reasonable interpretation of a Rumbaugh-style Object Model would not
allow subclasses to alter the nature of an association (at least, in
structural terms) which was established by a superclass.  If we were
tempted to do so, then I think we'd have to question the quality of
our analysis model (e.g., "does this association really apply to this
whole superclass?").

I'd say in general that each association, at least in terms of
structure, *would* be completely orthogonal to other parts of the
abstraction of a class, including the other associations and
value-oriented attributes.  If so, then it's perfectly natural to deal
with each association one at a time, whether you do so in child units
or separate association packages.

>    If any extension to the current draft standard is required in this
>area--and I don't think one is--

Nor do I, really. (Even despite my post on "package abstracts" -- that
was more of a thought experiment, not a serious recommendation or
proposal.)  My belief is that Ada9X is quite expressive enough, all we
need to do is find creative combinations of the facilities that are
already there.

>it would be to change 3.2.3(6) to make
>child units eligible as primitive operations, probably under the
>influence of a pragma.   I certainly don't recommend such a change to
>the standard--it would open up all sorts of holes--but that should
>prevent someone from experimenting with such a pragma.

Certainly would be an interesting experiment, but dangerous. (Wear
protective gear. ;-) The difficulty I see is that the primitive
interface of a tagged type (embodied in its tag, perhaps implemented
as a method jump table) would be a fluid thing, dependant on how many
child units were actually compiled and "withed" within a program.  It
seems that a compiler alone would not be able to resolve this, and
that it would have to be a function of a linker.

>--

>					Robert I. Eachus

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


-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------




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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-04 16:00             ` John Volan
@ 1994-10-05 11:42               ` Robert I. Eachus
  1994-10-05 21:09               ` Matt Kennel
  1 sibling, 0 replies; 43+ messages in thread
From: Robert I. Eachus @ 1994-10-05 11:42 UTC (permalink / raw)


In article <1994Oct4.160056.4243@swlvx2.msd.ray.com> jgv@swl.msd.ray.com (John Volan) writes:

  > This approach is nice, as long as we are willing to give up having
  > these operations be primitives for their respective tagged types.
  > I guess the only way I'd expand on this would be to make these into
  > child packages rather than just child functions.

  Yep, it may be a trade off, or it may be just what you wanted.  It
depends on whether the association is an integeral part of the
abstraction.  If it is, I'd use one of the other techniques.  But if
it is peripheral to the definiton of the class this is probably the
way to go.

  > Also, it looks like this scheme gives up on the possibility of
  > incorporating the pointers directly into the tagged record types
  > themselves, since they would still need to be declared in the private
  > parts of the root packages somehow.

    I'll answer this here, instead of with the "abstraction breaking"
comment.  If you implement the parent type with a explicit pointer to
an extension area, then the definition of that area can be deferred to
the package body.  Works fine, and you can even go through the
gymnastics of making the extensions a linked list of arbitrary fields
by having a private child export a generic which can be instantiated
for each different type of extension.

    The "problem" is that this gives a nice clean external interface
while having all sorts of complex structures "under the covers."
That's why the abstraction breaking comment.

  > I guess the real philosophical issue that everything is hinging on
  > here is: "Is a binary association *part* of the abstraction of
  > each of the classes, or is it a *separate* abstraction of its
  > own?"

   Change one word and you've got it exactly right: "Is THIS binary
association..."  You will almost certainly come up with different
answers in different cases, and Ada 9X supports--as we have just
seen--several abstractions, each appropriate for a different reality.

  > But a corollary to this issue is the question: "Do association
  > operations *need* to be primitives of each of the associated classes?"
  > I'm beginning to think not, but I'm not sure I have a good handle on
  > this question yet.  Any thoughts?

   Yep!  Some do, some should not be.  See above.

  > I'm not sure what you mean by "abstraction breaking".  Is there
  > some trick we could use to get those pointers into the tagged
  > records after all?  Perhaps "opaque" forms of the pointers that
  > somehow get converted to "transparent" forms later?

  Several solutions.  You hint at one which works.  You can have a
abstract class, and a type which is a pointer to all objects decended
from it:
 
  package Pointers is
   type Everything is abstract tagged null record;
   type Pointer is access all Everything'Class;
   ...
  end Pointers;

    Now you make Employees and Offices children (or grandchildren) of
everything, and on as before.  This looks like combining Office_Parent
and Employee_Parent from earlier examples, and in a sense it is.
However, replace Everything with Controlled, and it makes a lot more
sense. You lose some strong type legality checking, but good compilers
should be able to warn you of incorrect downcasts before run-time.
(Interesting side note here.  A good compiler integrated with an
annotation language would allow better compile time warnings, I don't
know that pragma ASSERT is sufficient.)

 > I don't think there's any problem here. It seems to me the most
 > reasonable interpretation of a Rumbaugh-style Object Model would not
 > allow subclasses to alter the nature of an association (at least, in
 > structural terms) which was established by a superclass.  If we were
 > tempted to do so, then I think we'd have to question the quality of
 > our analysis model (e.g., "does this association really apply to this
 > whole superclass?").

   Agreed, I was just trying to point out cases where this approach
would not be the right choice.

  > >it would be to change 3.2.3(6) to make
  > >child units eligible as primitive operations, probably under the
  > >influence of a pragma.   I certainly don't recommend such a change to
  > >the standard--it would open up all sorts of holes--but that should
  > >prevent someone from experimenting with such a pragma.

 > Certainly would be an interesting experiment, but dangerous. (Wear
 > protective gear. ;-) The difficulty I see is that the primitive
 > interface of a tagged type (embodied in its tag, perhaps implemented
 > as a method jump table) would be a fluid thing, dependant on how many
 > child units were actually compiled and "withed" within a program.  It
 > seems that a compiler alone would not be able to resolve this, and
 > that it would have to be a function of a linker.

   In general, the linker is going to have to do part of the building
of jump tables now, so that is not a drawback.  The problems would
occur in the implementation of compile time overload resolution,
especially for non-tagged types. (Ouch!)  An experimental compiler
could make all such bindings non-static and resolved through jump
tables, but a usable compiler that only used jump tables where
necessary would be an interesting research project.  (Compiling a
child unit into the library might have to result in recompiling units
which depend on the parent, even if they are unaware of the child.
The old "legal with respect to units they do not depend on (see
AI-256)" Pandorra's box.)

    This discussion has been very interesting. I'm beginning to think
that we are starting to understand this new language.  From what I
have seen it is going to be a very nice place to live.


--

					Robert I. Eachus

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



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

* Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)
  1994-10-04 16:00             ` John Volan
  1994-10-05 11:42               ` Robert I. Eachus
@ 1994-10-05 21:09               ` Matt Kennel
  1 sibling, 0 replies; 43+ messages in thread
From: Matt Kennel @ 1994-10-05 21:09 UTC (permalink / raw)


John Volan (jgv@swl.msd.ray.com) wrote:
: I guess the real philosophical issue that everything is hinging on
: here is: "Is a binary association *part* of the abstraction of each of
: the classes, or is it a *separate* abstraction of its own?"

: Adam Beneschan objected to other solutions that put the association
: structure in a common root package, on the grounds that nothing from
: the "real world" was being modeled by that.  I disagree with that, and
: agree with Magnus Kempe that in fact the association should be treated
: as a first-class abstraction of its own, in some fashion.

I think it's somewhat more than just a 'philosophical' issue.

If you have a "thing" managing the mutual couplings, you can
easily guarantee that

 employee(office(this_employee_object)) = this_employee_object.
 (and the reverse).

i.e. that you always maintain an invertible one-to-one relation.

I thought this was one of the original requirements.

If you have a 'thing' relating the two, you can put this
guarantee logic in one place, instead of having this fact be
implicitly relying upon the proper programming of the classes
to be related.

--
-Matt Kennel  		mbk@inls1.ucsd.edu
-Institute for Nonlinear Science, University of California, San Diego
-*** AD: Archive for nonlinear dynamics papers & programs: FTP to
-***     lyapunov.ucsd.edu, username "anonymous".



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

end of thread, other threads:[~1994-10-05 21:09 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1994-09-27 16:52 Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) John Volan
1994-09-27 18:48 ` Mark A Biggar
1994-09-29  1:46   ` John Volan
1994-09-29 13:57     ` Tucker Taft
1994-09-29 17:20       ` Bjarne Stroustrup <9758-26353> 0112760
1994-09-30  1:38         ` Tucker Taft
1994-09-30 12:33           ` Bjarne Stroustrup <9758-26353> 0112760
1994-09-29 18:37       ` John Volan
1994-09-29 19:34         ` David Weller
1994-09-30 22:13           ` John Volan
1994-10-02  3:31             ` Andrew Lees
1994-09-30  1:47         ` Tucker Taft
1994-09-30 13:30           ` John Volan
1994-09-29 18:10     ` R. William Beckwith
1994-10-03  0:33     ` Cyrille Comar
1994-09-28 14:01 ` Norman H. Cohen
1994-09-29  2:12   ` John Volan
1994-09-29 14:01     ` Tucker Taft
1994-09-29 18:37     ` Norman H. Cohen
1994-09-29  9:48   ` Magnus Kempe
1994-09-29 13:10     ` Magnus Kempe
1994-09-29 18:05       ` Tucker Taft
1994-09-30 10:20         ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
1994-09-30 13:22           ` Tucker Taft
1994-10-01  1:24       ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Adam Beneschan
1994-10-01 12:01         ` Magnus Kempe
1994-10-01 18:43         ` Mark A Biggar
1994-10-02 16:41         ` John Volan
1994-10-02 23:33           ` Matt Kennel
1994-10-03  8:07           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? Magnus Kempe
1994-10-03 12:14           ` Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Robert I. Eachus
1994-10-04  2:12             ` R. William Beckwith
1994-10-04 16:00             ` John Volan
1994-10-05 11:42               ` Robert I. Eachus
1994-10-05 21:09               ` Matt Kennel
1994-10-03 20:29           ` Harry Koehnemann
1994-09-29 13:35     ` John Volan
1994-09-30 20:27       ` Norman H. Cohen
1994-10-01  1:47         ` John Volan
1994-10-01 20:44           ` Tucker Taft
1994-10-03 11:29           ` Robert I. Eachus
1994-09-30 22:46       ` Matt Kennel
1994-10-01  2:11         ` John Volan

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