From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,56dbd715fa74735a X-Google-Attributes: gid103376,public From: Matthew Heaney Subject: Re: Mutually dependent private types Date: 1998/05/27 Message-ID: X-Deja-AN: 357221237 References: <6k25ra$6j7$1@nnrp1.dejanews.com> <3565B105.9BFB4788@ac3i.dseg.ti.com> <356B226F.EF05E927@ac3i.dseg.ti.com> <356C8A02.44354B09@ac3i.dseg.ti.com> Organization: Network Intensive Newsgroups: comp.lang.ada Date: 1998-05-27T00:00:00+00:00 List-Id: John Volan 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 (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.