comp.lang.ada
 help / color / mirror / Atom feed
From: Matthew Heaney <matthew_heaney@acm.org>
Subject: Re: Mutually dependent private types
Date: 1998/05/27
Date: 1998-05-27T00:00:00+00:00	[thread overview]
Message-ID: <m3g1hvs1u3.fsf@mheaney.ni.net> (raw)
In-Reply-To: 356C8A02.44354B09@ac3i.dseg.ti.com


John Volan <johnv@ac3i.dseg.ti.com> writes:

> I believe the example I used in the past was:
> 
>     Manager -- supervises -- Employee
>     Manager -- is a kind of -- Employee
> 
> This is a case where you might have a mutual dependency between a
> generalization (Employee) and one of its own specializations (Manager). 
> You need Ada95's inheritance mechanism to implement the
> generalization/specialization relationship; but this might collide with
> resolving the mutual dependency.

[snip first cut] 

> Here's the second cut, where we run into inheritance collision:
> 
> package Employees_Forward is
>   type Employee_Forward_Type is abstract tagged limited null record;
>   type Employee_Access_Type is access all Employee_Forward_Type'Class;
> end Employees_Forward;
> 
> package Managers_Forward is
>   type Manager_Forward_Type is abstract tagged limited null record;
>   type Manager_Access_Type is access all Manager_Forward_Type'Class;
> end Managers_Forward;
> 
> with Employees_Forward;
> with Sets;
> package Employee_Sets is
>   new Sets (Element => Employees_Forward.Employee_Access_Type);
> 
> with Employees_Forward;
> with Managers_Forward;
> package Employees is  
>   type Employee_Type is 
>     abstract new Employees_Forward.Employee_Forward_Type with private;
>   function Get_Supervisor (Employee : in Employee_Type)
>     return Managers_Forward.Manager_Access_Type;
>   ...
> end Employees;
> 
> with Managers_Forward;
> with Employees;
> with Employee_Sets;
> package Managers is
> 
>   type Manager_Type is 
>     -- Need this to resolve the mutual dependency:
>     new Managers_Forward.Manager_Forward_Type with private;
>     -- But need this to implement the generalization/specialization:
>     new Employees.Employee_Type with private;
>     -- Inheritance Collision!
> 
>   function Get_Subordinates (Manager : in Manager_Type)
>     return Employee_Sets.Set_Type;
>   ...
> end Managers;

Let's analyze this.

1)  Why do you need two forward declarations?  You only need to cut the
circle once, and so only N-1 forward declarations are required.

2)  The package hierarchy should match the type hierarchy.  Type Manager
derives from Employee, so why isn't package Managers a child of
Employees?

3)  Implementing 2) obviates the need for step 1), since Manager can
derive from Employee directly.

The only thing you'll have to do - and this is where you may object - is
do forward declare a root manager type in Employee, to provide the
return value of the Get_Supervisor function.  

Here is code that implements these ideas:
 
package Employees is

   type Employee_Type is tagged private;


   type Root_Manager_Type is
     new Employee_Type with private;

   type Manager_Access_Type is
      access all Root_Manager_Type'Class;


   function Get_Supervisor
     (Employee : in Employee_Type)
     return Manager_Access_Type;

private

   type Employee_Type is
      tagged record
        Manager : Manager_Access_Type;
      end record;

   type Root_Manager_Type is
      new Employee_Type with null record;

end Employees;

with Employees.Sets;
package Employees.Managers is

  type Manager_Type is
    new Root_Manager_Type with private;

    function Get_Subordinates
      (Manager : in Manager_Type)
       return Employees.Sets.Set_Type;

private

   type Manager_Type is
      new Root_Manager_Type with record
         Set : Employees.Sets.Set_Type;
      end record;

end Employees.Managers;

Doesn't this do what you want?  Where's the collision?

 
> To resolve the inheritance collision, we have to do some monkeying
> around:
> 
> with Employees_Forward;
> package Employees is  
>   type Employee_Type is 
>     abstract new Employees_Forward.Employee_Forward_Type with private;
>     -- actually, don't really need Employees_Forward unless there's
>     -- a mutual dependency with something else...
>   type Employee_Access_Type is access all Employee_Type'Class;
> 
>   type Manager_Forward_Type is
>     abstract new Employee_Type with private;
>   type Manager_Access_Type is access all Manager_Forward_Type'Class;
> 
>   function Get_Supervisor (Employee : in Employee_Type)
>     return Manager_Access_Type;
>   ...
> end Employees;

Oh!  This is essentially what I have above, so we're more or less
on the same wavelength.

> with Employees;
> with Employee_Sets;
> package Managers is
>   type Manager_Type is 
>     new Employees.Manager_Forward_Type with private;
>   function Get_Subordinates (Manager : in Manager_Type)
>     return Employee_Sets.Set_Type;
>   ...
> end Managers;

Again, more or less the same.

> Following Norman Cohen's scheme, we had a nice pattern going there with
> those *_Forward packages.  But to avoid inheritance collision, we had to
> break this pattern and move the Manager_Forward_Type from the
> Managers_Forward package into the Employees package.

Funny, I don't call this organization of types "avoiding inheritance
collision by declaring Manager_Forward_Type in Employees package."  I
just call it "object-oriented programming."  I wouldn't have thought
about doing it any other way, as it seems perfectly natural to me.

This is not unlike the example in my Active Iterators post a week or two
ago.  You have to "forward declare" the Root_Iterator type right there
in the same package as the root stack, ie

generic
...
package Stacks is

   type Root_Stack is abstract tagged ...;

   type Root_Iterator is abstract tagged limited ...;

   type Iterator_Access is access all Root_Iterator'Class;

   function New_Iterator 
      (Stack : Root_Stack) return Iterator_Access;
...

How else would you do it?  This seems like the correct, most natural
place to declare Root_Iterator.

You don't like declaring the Root_Manager type in the Employees package,
but I don't consider this to be a "problem" at all.

> By rights, forward type declarations ought to be a language feature
> that's totally orthogonal to inheritance and polymorphism.  This would
> indeed be true in Tucker's "with type" proposal:
> 
> with type Managers.Manager_Type;
> package Managers_Access is
>   type Manager_Access_Type is access all Managers.Manager_Type'Class;
> end Managers_Access;

At first blush I don't think that "with type" is the way to go.  

The problem is that it conflicts with an already established pattern wrt
to "with" and "use".  A neophyte programmer is taught that get access to
a package, you "with" the package.  And then to get direct visibility to
the declarations there, to "use" that package.

So the pattern

with P; use P;

becomes ingrained.  So now when he sees this

with type P.T;

he's going to think that he can say

with type P.T; use type P.T;

to get direct visibility to the primitive operators of the type P.T.

But this is incorrect, because "use type P.T" already means something
else.  Right?

I don't know what to do really.  Maybe a pragma be better:

with P; 
pragma Mutual (P);

package Q is

  type QT is ...;

  procedure QOp (QO : QT; PO : access P.T);
...
end Q;

Actually, I'd like it _much_ better if I didn't have to pass P.T as an
access param.  Knowing that P.T is tagged (or just passed by reference),
I'd like to state that in Q, so I can do this

with P;
pragma Mutual_Tagged (P.T);

package Q is

   type QT is ...;
  
   procedure QOp (QO : QT; PO : P.T);
...

No access parameter is required.

I don't know how to handle the case of a type that is only privately
tagged, however.  Maybe we can allow that aspect of a private type to be
stated publically, in the form of a pragma:

with P;
pragma Mutual_By_Reference (P.T);
package Q is ...;


with Q;
pragma <whatever> (Q);
package P is

   type T is private;
...
private

   type T is new Controlled with ...;

end P;


That would mean that 

...
package P is

   type T is limited private;
...
private

   type T is new Integer;

end P;

would be illegal for Mutual_By_Reference usage by Q, because T isn't
passed by reference.





  reply	other threads:[~1998-05-27  0:00 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-05-21  0:00 Mutually dependent private types adam
1998-05-21  0:00 ` John Volan
1998-05-21  0:00 ` Matthew Heaney
1998-05-22  0:00   ` John Volan
1998-05-22  0:00     ` Matthew Heaney
1998-05-26  0:00       ` Robert I. Eachus
1998-05-26  0:00         ` John Volan
1998-05-27  0:00           ` Robert I. Eachus
1998-05-29  0:00             ` John Volan
1998-05-27  0:00           ` Jerry van Dijk
1998-05-29  0:00             ` John Volan
1998-05-26  0:00       ` John Volan
1998-05-26  0:00         ` Matthew Heaney
1998-05-27  0:00           ` John Volan
1998-05-27  0:00             ` Matthew Heaney [this message]
1998-05-28  0:00               ` John Volan
1998-05-28  0:00                 ` Matthew Heaney
1998-05-29  0:00                   ` John Volan
1998-05-29  0:00                 ` Brian Rogoff
1998-05-29  0:00                   ` John Volan
1998-05-29  0:00                     ` Brian Rogoff
1998-05-29  0:00                       ` John Volan
1998-05-30  0:00                 ` Geoff Bull
1998-05-30  0:00                   ` Fergus Henderson
1998-06-01  0:00                     ` John Volan
1998-06-02  0:00                       ` Fergus Henderson
1998-06-04  0:00                       ` Robert Dewar
  -- strict thread matches above, loose matches on Subject: below --
1998-05-22  0:00 adam
1998-05-22  0:00 ` Matthew Heaney
1998-05-22  0:00 ` Brian Rogoff
1998-05-22  0:00 ` John Volan
replies disabled

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