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: John Volan Subject: Re: Mutually dependent private types Date: 1998/05/26 Message-ID: <356B6D65.BD34E3EF@ac3i.dseg.ti.com> X-Deja-AN: 356824353 Content-Transfer-Encoding: 7bit References: <6k25ra$6j7$1@nnrp1.dejanews.com> <3565B105.9BFB4788@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-26T00:00:00+00:00 List-Id: Robert I. Eachus wrote: > > In article matthew_heaney@acm.org (Matthew Heaney) writes: > > > Furthermore, this split is only required when there's mutual dependency. > > Most types are definitely not mutually dependent, and so this workaround > > should only seldom be necessary. > > I would argue, from experience, that every time I have found an > apparent need for this mutual dependency, it indicates I have not > fully studied the problem. For example in John's example of > employees and offices, it quickly becomes obvious that not all rooms > are offices, and not all people are employees, so you end up needing > at least one of the parent classes for other reasons. Hold on a minute there! You're falling into the "reality" fallacy: That's where one relies too heavily on philosophical (and supposedly "objective") knowledge about "real-world" objects to guide the design of software objects. Software objects shouldn't be grab-bags of irrelevant domain information, they should be encapsulations of the actual responsibilities of an actual target system. Suppose I were to tell you that, for a certain target application, the concepts of "employee" and "office" were entirely sufficient to encapsulate the responsibilities of that application; and that the notion of "person" as a generalization for "employee", and "room" as a generalization for "office", were totally unnecessary and irrelevant. In other words, that system has lots of functional requirements that apply to "employees" and "offices" specifically, but absolutely no requirements that apply to "people" and "rooms" in general. But suppose we still had an unresolvable mutual dependency between the "employee" and "office" classes. Under those circumstances, introducing a "Person_Type" and deriving "Employee_Type" from it would be specious; you might as well just have an "Employee_Forward_Type" and derive "Employee_Type" from that. But suppose for the sake of argument we consider a somewhat different target application, one where the "person" and "room" generalizations are relevant, and we do have some functional requirements that apply to those generalizations. For instance, our application is keeping track of "employees" and "customers", which are both kinds of "persons", and there's something common that we do for all "persons"; likewise, the application tracks "offices", "meeting rooms", "restrooms", etc., all as specializations of "room", and there's something common we do for all "rooms". But let's say we still have other functional requirements that apply to "employee" and "office" specifically, and among those functional requirements there's still a mutual dependency between "employee" and "office". For instance, let's say we can assign an office to an employee, for use as that employee's everyday workspace. For every office we keep track of the assigned employee, and for every employee, we keep track of the assigned office. But an assigned employee has to be an employee, not just any kind of person -- for instance, you can't assign an office to a customer. And an assigned office has to be an office, it can't just be any kind of room -- for instance, you can't put an employee's desk in a meeting room or a restroom. (Well, you might, but that employee wouldn't stay an employee for long... :-) Given the above requirements, the natural thing to do would be to declare the following packages, but they're unfortunately illegal because of the mutual spec dependency: with Persons; with Offices; package Employees is type Employee_Type is new Persons.Person_Type with private; procedure Assign_Office (Employee : in out Employee_Type; Office : in out Offices.Office_Type'Class); function Get_Assigned_Office (Employee : in Employee_Type) return Offices.Office_Type'Class; ... end Employees; with Rooms; with Employees; package Offices is type Office_Type is new Rooms.Room_Type with private; procedure Assign_Employee (Office : in out Office_Type; Employee : in out Employees.Employee_Type'Class); function Get_Assigned_Employee (Office : in Office_Type) return Employees.Employee_Type'Class; ... end Offices; The following avoids the mutual spec dependency, but it loses something: with Persons; with Rooms; package Employees is type Employee_Type is new Persons.Person_Type with private; procedure Assign_Office (Employee : in out Employee_Type; Office : in out Rooms.Room_Type'Class); -- precondition: Office in Offices.Office_Type'Class function Get_Assigned_Office (Employee : in Employee_Type) return Rooms.Room_Type'Class; -- postcondition: return result in Offices.Office_Type'Class; ... end Employees; with Rooms; with Persons; package Offices is type Office_Type is new Rooms.Room_Type with private; procedure Assign_Employee (Office : in out Office_Type; Employee : in out Persons.Person_Type'Class); -- precondition: Employee in Employees.Employee_Type function Get_Assigned_Employee (Office : in Office_Type) return Persons.Person_Type'Class; -- postcondition: return result in Employees.Employee_Type'Class ... end Offices; You might as well go with an approach a la Norman Cohen: with Persons; package Employees_Forward is type Employee_Forward_Type is abstract new Persons.Person_Type with null record; end Employees_Forward; with Rooms; package Offices_Forward is type Office_Forward_Type is abstract new Rooms.Room_Type with null record; end Offices_Forward; with Employees_Forward; with Offices_Forward; package Employees is type Employee_Type is new Employees_Forward.Employee_Forward_Type with private; procedure Assign_Office (Employee : in out Employee_Type; Office : in out Offices_Forward.Office_Forward_Type'Class); function Get_Assigned_Office (Employee : in Employee_Type) return Offices_Forward.Office_Forward_Type'Class; ... end Employees; with Offices_Forward; with Employees_Forward; package Offices is type Office_Type is new Offices_Forward.Office_Forward_Type with private; procedure Assign_Employee (Office : in out Office_Type; Employee : in out Employees_Forward.Employee_Forward_Type'Class); function Get_Assigned_Employee (Office : in Office_Type) return Employees_Forward.Employee_Forward_Type'Class; ... end Offices; > Note also that once both types have been declared, the needed > operations with the static checks can be declared, you just have this > annoying artifact of a visible function which is implicitly required > to do a static check at run time. What really is needed is a language > independent way to "cast away 'CLASS" and move the check from within > the subprogram to a (required) static type check at the location of > the call. This certainly can be done by a pragma. In fact you would > think that pragma Restrictions (No_Dispatch) would do precisely that, > but it is too restrictive, outlawing all cases of 'CLASS. > > So I suggest that we need a pragma Static_Dispatch which directs > the compiler to eliminate dispatching tables for the named unit and to > reject any unit which calls the unit in a way that requires > dispatching. Of course, if the unit is called from within any other > primitive operation of the type, that operation will also require the > pragma, so maybe a better solution is to have the pragma apply to (all > the primitive operations of) a type, but not to primitive operations > of types derived from that type. Since its indended use is for those, > usually abstract, parent types in situations like this, the effect > would be to outlaw dispatching call using Person'Class or Room'Class, > but allow dispatching on Employee'Class or Office'Class. This makes no sense to me. The entire raison d'etre for abstract classes such as Person and Room would be precisely for polymorphic dynamic dispatching! Surely the kind of pragma you suggest should be applied to "spurious" superclasses that are meant to act merely as forward type declarations, e.g., Employee_Forward_Type and Office_Forward_Type. But then neither of those Forward_Types would have any primitives. The whole point to the Forward_Types was to defer defining any primitives for employees or offices until the actual types could be declared. -- 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. :-)" );