comp.lang.ada
 help / color / mirror / Atom feed
From: John Volan <johnv@ac3i.dseg.ti.com>
Subject: Re: Mutually dependent private types
Date: 1998/05/28
Date: 1998-05-28T00:00:00+00:00	[thread overview]
Message-ID: <356E09A1.B493FE89@ac3i.dseg.ti.com> (raw)
In-Reply-To: m3g1hvs1u3.fsf@mheaney.ni.net


Matthew Heaney wrote:
> 
> 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.

Two reasons:

(1) Symmetry: Why cut the circle in one direction and not the other? In
most bi-directional associations, there's no a priori reason to bias the
design in a particular direction, so any such biasing tends to be
arbitrary. Ada95's "with"-ing problem seems to force programmers into
favoring an asymmetrical solution, whereas other languages tolerate a
perfectly symmetrical pattern.  I will concede though that an
inheritance relationship between two classes does introduce a bias (in
Ada95): In order to derive Manager_Type from Employee_Type, Manager_Type
needs to see Employee_Type directly, not just a forward declaration of
Employee_Type. However, as I've said before, the issue of mutual
dependency to me is orthogonal to the issue of inheritance and
polymorphism, so why should the fact that two classes happen to be
related by inheritance affect the way mutual dependency is resolved?

(2) Scalability: It's short-sighted to think that this would be the only
"circle" that these particular classes might be participating in. 
Declaring Employee_Forward_Type and Manager_Forward_Type doesn't just
help solve the mutual dependency dilemma between those two types, it
solves any mutual dependency dilemma with any other third-party type
that might come along.  But why should such a third-party type have to
look in package Employees for a Manager_Forward_Type, when Norman
Cohen's pattern would suggest it should be found in a Managers_Forward
package?

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

This is an entirely orthogonal issue you're bringing up.  Should a type
hierarchy always be mirrored by the package hierarchy that encapsulates
it?  No, not necessarily, IMHO.  Ada95 is a modular language: Type
hierarchies support issues of polymorphism and inheritance, whereas
package hierarchies support issues of visibility and namespace control. 
A derived type should be declared in a child package *if* you want it to
have visibility to its parent type's implementation details; it should
be declared in a different package if you *don't* want it to have that
visibility.  Whether you want to grant such visibility or not ...
depends.  It's somewhat analogous to the question, in other OO
languages, of whether to make a certain feature of a class "private"
(visible to absolutely nobody else) or "protected" (visible only to
subclasses).  It's a design choice.  But that's a very different issue
from mutual spec dependency.

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

I don't see how 2) has anything to do with it.  Manager_Type can derive
from Employee_Type regardless of whether the Managers package is a child
of the Employees package, or whether it simply "withs" the Employees
package. The only difference is whether private stuff about
Employee_Type is directly visible to Manager_Type.

> 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.

[snip Matt's example, essentially the same as mine...]

> Doesn't this do what you want?  Where's the collision?
> 
> > To resolve the inheritance collision, we have to do some monkeying
> > around:

[snip my example...]

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

[snip]

> > 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.

Look, these days the term "object-oriented" has become so overused that
its meaning has been reduce to little more than "good".  When someone
wants to indicate that their favorite idiom is "good", they'll label it
"object-oriented".  So basically what you're saying is, you think it's
"good programming" to use inheritance as a way of simulating a forward
type declaration, even if inheritance collision forces you to break the
pattern and put a forward type declaration in an unexpected place. 
Well, that's your opinion, but I have to disagree with it.

> 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.

I don't think these cases are entirely comparable.  First of all, your
Stacks package is a generic, which throws a whole new factor into the
equation.  Separately encapsulating two abstractions is a lot harder
when they both have to be governed by the same generic formal
parameters.  (To some extent, it can still be done: You can have two
packages, but they'd both have to be nested inside the generic package.)

Secondly, the Employee and Manager classes are related to each other by
inheritance, but aside from that they are as loosely coupled to each
other (conceptually) as any other pair of problem-domain classes (for
instance, Employee and Office).  Stack and Iterator are not related to
each other by inheritance, but despite that they are very tightly
coupled to each other.  In fact, to implement Stack and Iterator, it's
likely that both classes will need access to each other's implementation
details, which argues for putting them in the same package. But I don't
see any need for the Manager subclass to *necessarily* have access to
private details about the Employee superclass, or vice versa.  (They may
or may not need such access, depending on other factors in the design,
but not simply because there's a mutual spec dependency. The assumption
in my examples has been that the classes involved *don't* need access to
each other's implementation details.)

> 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.

Come now, that's like objecting to using "for" in a rep spec in a
declarative part because that would confuse neophytes who are used to
using "for" in a loop statement in a statement sequence.  You realize,
of course, that it was Tucker Taft himself who originally came up with
the "with type" idea.  If anybody is a good judge of what language
extensions would fit well into the Ada scheme of things, I'd imagine
he'd be right up there... :-)

> So the pattern
> 
> with P; use P;
> 
> becomes ingrained.  

Who says? There are at least as many Ada folks who would vigorously
discourage a neophyte from getting into the "use" habit as there are
those who would encourage him! (Probably a lot more.)  Anyway, neophytes
and expert programmers alike should be encouraged NOT to program simply
from ingrained habit, but from a considered understanding of the
rationale and tradeoffs behind the various constructs and idioms
available in a given language.

> 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?

No, it means exactly "direct visibility to the primitive operators of
the type P.T".  This situation was actually considered when "with type"
was first discussed. I believe the conclusion was to allow "use type"
but to make it ineffectual, on the grounds that there aren't yet any
primitive P.T operators to make visible, since P.T is still just an
incomplete type declaration at this point. 

> I don't know what to do really.  Maybe a pragma be better:
> 
> with P;
> pragma Mutual (P);
> package Q is

It seems to me that if there's a mutual spec dependency going on between
P and Q, then the "with" clause here is already illegal according to
Ada95's language rules.  So what exactly would this "Mutual" pragma
mean?  "I know the with clause is illegal, but ignore that and pretend
to be a different language"? :-)  What about Ada95's run-time
elaboration mechanism?  When would the elaboration code for the specs of
packages P and Q get executed? In what order?

> 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);

See
http://bluemarble.net/~jvolan/WithingProblem/FAQ.html#liberal_incompletes
for a discussion of Tucker's proposal to liberalize the rules for
incomplete types,  to allow their use for parameters and return types. 
The idea is that you could go ahead and spec out a subprogram using a
"with type", but when you actually write the subprogram body, or
anywhere you actually call the subprogram, you have to do a full "with"
clause to make the full type declaration available.  Bottom line, when
the compiler really needs to know exactly how to pass the parameters, it
can know.

-- 
Signature volanSignature = 
  new Signature
  ( /*name:      */ "John G. Volan",
    /*employer:  */ "Raytheon Advanced C3I Systems, San Jose",
    /*workEmail: */ "johnv@ac3i.dseg.ti.com",
    /*homeEmail: */ "johnvolan@sprintmail.com",
    /*selfPlug:  */ "Sun Certified Java Programmer",
    /*twoCents:  */ "Java would be even cooler with Ada95's " +
                    "generics, enumerated types, function types, " +
                    "named parameter passing, etc...",
    /*disclaimer:*/ "These views not packaged in COM.ti.dseg.ac3i, " +
                    "so loading them throws DontQuoteMeError. :-)" );




  reply	other threads:[~1998-05-28  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
1998-05-28  0:00               ` John Volan [this message]
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