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.
next prev parent 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