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=-0.9 required=5.0 tests=BAYES_00,YOU_INHERIT autolearn=no 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: John Volan Subject: Re: Mutually dependent private types Date: 1998/05/27 Message-ID: <356C8A02.44354B09@ac3i.dseg.ti.com> X-Deja-AN: 357089155 Content-Transfer-Encoding: 7bit References: <6k25ra$6j7$1@nnrp1.dejanews.com> <3565B105.9BFB4788@ac3i.dseg.ti.com> <356B226F.EF05E927@ac3i.dseg.ti.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Organization: Raytheon Systems Company, Advanced C3I Systems Newsgroups: comp.lang.ada Date: 1998-05-27T00:00:00+00:00 List-Id: Matthew Heaney wrote: > > John Volan writes: > > > Furthermore, what if you're already using inheritance to support true > > polymorphism, and then you discover you need to break a mutual > > dependency somewhere in your class hierarchy? You might end up with a > > situation I call "inheritance collision": That's where you're trying to > > apply Ada's inheritance mechanism for two competing purposes, but you're > > faced with the fact that Ada only supports single inheritance. Sure, > > it's possible to wiggle your inheritance hierarchy around so that it'll > > work and you can still keep your mutually-dependent types in different > > packages. But I find that to be awkward and artificial. > > If you have deep or otherwise "complex" type hierarchies in Ada, then > you're doing something wrong. Hierarchies should be shallow. > > When I hear stories about problems someone is having with their > inheritance hierarchies, I become suspicious, because it often indicates > that the programmer is fighting the langauge. While I wholeheartedly agree with this sentiment, it has little to do with the situation we have here. Sure, if an inheritance collision is caused by a flawed analysis of the problem domain, you should question the designer's choice of classes. But here we're talking about inheritance collision caused by the attempt to apply Ada95's inheritance mechanism to resolve a mutual dependency *while at the same time* using it to implement a "legitimate" case of polymorphism -- "legitimate" because it's motivated by a real case of generalization/specialization in the problem domain. This polymorphism hierarchy could be quite simple and shallow and meet any criterion of "goodness" you can name, but the mutual dependency could still collide with it. 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. Here's our first cut, where we run into the illegal mutual spec dependency: with Managers; -- illegal package Employees is type Employee_Type is abstract tagged limited private; type Employee_Access_Type is access all Employee_Type'Class; function Get_Supervisor (Employee : in Employee_Type) return Managers.Manager_Access_Type; ... end Employees; with Sets; with Employees; package Employee_Sets is new Sets (Element => Employees.Employee_Access_Type); with Employees; -- illegal package Managers is type Manager_Type is new Employees.Employee_Type with private; type Manager_Access_Type is access all Manager_Type'Class; function Get_Subordinates (Manager : in Manager_Type) return Employee_Sets.Set_Type; ... end Managers; 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; 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; 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; 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. 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; with type Employees.Employee_Type; package Employees_Access is type Employee_Access_Type is access all Employees.Employee_Type'Class; end Employees_Access; with Employees_Access; with Sets; package Employee_Sets is new Sets (Element => Employees_Access.Employee_Access_Type); with Managers_Access; package Employees is type Employee_Type is abstract tagged limited private; function Get_Supervisor (Employee : in Employee_Type) return Managers_Access.Manager_Access_Type; ... end Employees; with Employees; with Employee_Sets; package Managers is type Manager_Type is new Employees.Employee_Type with private; function Get_Subordinates (Manager : in Manager_Type) return Employee_Sets.Set_Type; ... end Managers; As a workaround, my generic Forward package also manages to keep itself orthogonal to inheritance: with Forward; package Employees_Forward is new Forward; with Forward; package Managers_Forward is new Forward; with Employees_Forward; with Sets; package Employee_Sets is new Sets (Element => Employees_Forward.Reference_Type); with Employees_Forward; with Managers_Forward; package Employees is package Forward renames Employees_Forward; type Employee_Type is abstract tagged limited private; type Employee_Access_Type is access all Employee_Type'Class; function Get_Supervisor (Employee : in Employee_Type) return Managers_Forward.Reference_Type; ... end Employees; with Managers_Forward with Employees; with Employee_Sets; package Managers is package Forward renames Managers_Forward; type Manager_Type is new Employees.Employee_Type with private; type Manager_Access_Type is access all Manager_Type'Class; function Get_Subordinates (Manager : in Manager_Type) return Employee_Sets.Set_Type; ... end Managers; package Employees.Binding is new Employees.Forward.Binding (Employee_Type, Employee_Access_Type); package Managers.Binding is new Managers.Forward.Binding (Manager_Type, Manager_Access_Type); But, although you can twist Norman Cohen's technique around so that it will still work even in the most extreme case, the fact that you have to do that shows that it isn't a very orthogonal workaround. -- 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. :-)" );