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.8 required=5.0 tests=BAYES_00,INVALID_DATE autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 1108a1,66253344eaef63db X-Google-Attributes: gid1108a1,public X-Google-Thread: 103376,66253344eaef63db X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-10-04 09:37:59 PST Newsgroups: comp.lang.ada,comp.object Path: bga.com!news.sprintlink.net!howland.reston.ans.net!europa.eng.gtefsd.com!MathWorks.Com!news2.near.net!noc.near.net!ray.com!news.ray.com!news.ed.ray.com!swlvx2!jgv From: jgv@swl.msd.ray.com (John Volan) Subject: Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Date: Tue, 4 Oct 1994 16:00:56 GMT Message-ID: <1994Oct4.160056.4243@swlvx2.msd.ray.com> References: <1994Sep27.165203.9192@swlvx2.msd.ray.com> <36bt0c$17oo@watnews1.watson.ibm.com> <1994Sep29.103358@di.epfl.ch> <36eebb$jn5@disunms.epfl.ch> <1994Oct2.164105.13127@swlvx2.msd.ray.com> Sender: news@swlvx2.msd.ray.com (NEWS USER) Organization: Raytheon Company, Tewksbury, MA Xref: bga.com comp.lang.ada:6450 comp.object:7045 Date: 1994-10-04T16:00:56+00:00 List-Id: eachus@spectre.mitre.org (Robert I. Eachus) writes: >This one has a very simple solution: child units. >This eliminates the "necessity" for mutual recursion in the visible >package specifications. > However, there are limitations to this approach. (Let me do a bit >of a sketch here: > package Employees is > type Employee is tagged private; > ... > end Employees; > package Offices is > type Office is tagged private; > ... > end Offices; > with Offices; use Offices; > function Employees.Assigned_Office(E: in Employee) return Office; > with Employees; use Employees; > function Offices.Occupant(O: in Office) return Employee; This approach is nice, as long as we are willing to give up having these operations be primitives for their respective tagged types. I guess the only way I'd expand on this would be to make these into child packages rather than just child functions. Also, it looks like this scheme gives up on the possibility of incorporating the pointers directly into the tagged record types themselves, since they would still need to be declared in the private parts of the root packages somehow. However, this scheme could be combined very nicely with the approach Matt Kennel suggested of off-loading the association itself to a third package that implemented some kind of dual hash-table: ---------------------------------------------------------------------- generic type Former (<>) is limited private; type Former_Pointer is access all Object'Class; type Latter (<>) is limited private; type Latter_Pointer is access all Object'Class; package One_To_One is function Latter_Of (The_Former : in Former_Pointer) return Latter_Pointer; function Former_Of (The_Latter : in Latter_Pointer) return Former_Pointer; procedure Associate ( The_Former : in Former_Pointer; The_Latter : in Latter_Pointer ); procedure Dissociate ( The_Former : in Former_Pointer ); procedure Dissociate ( The_Latter : in Latter_Pointer ); end One_To_One; -- Implementation of this is left as an exercise for the reader ... :-) ---------------------------------------------------------------------- with Employee; with Office; with One_To_One; package Employee_Occupies_Office is new One_To_One (Former => Employee.Object, Former_Pointer => Employee.Pointer, Latter => Office.Object, Latter_Pointer => Office.Pointer); ---------------------------------------------------------------------- with Office; with Employee_Occupies_Office; package Employee.Office_Occupied is -- Employee.Office_Occupied.That_Of function That_Of ( The_Employee : in Employee.Object'Class ) return Office.Pointer; -- Employee.Office_Occupied.Associate procedure Associate ( The_Employee : in out Employee.Object'Class; New_Office : in Office.Pointer ); -- Employee.Office_Occupied.Dissociate procedure Dissociate ( The_Employee : in out Employee.Object'Class ); end Employee.Office_Occupied; ---------------------------------------------------------------------- package body Employee.Office_Occupied is -- Employee.Office_Occupied.That_Of function That_Of ( The_Employee : in Employee.Object'Class ) return Office.Pointer is begin Employee_Occupies_Office.Latter_Of (The_Former => The_Employee'Access); end That_Of; -- Employee.Office_Occupied.Associate procedure Associate ( The_Employee : in out Employee.Object'Class; New_Office : in Office.Pointer ) is begin Employee_Occupies_Office.Associate ( The_Former => The_Employee'Access, The_Latter => New_Office ); end Associate; -- Employee.Office_Occupied.Dissociate procedure Dissociate ( The_Employee : in out Employee.Object'Class ) is begin Employee_Occupies_Office.Dissociate (The_Former => The_Employee'Access); end Dissociate; end Employee.Office_Occupied; ---------------------------------------------------------------------- with Employee; with Employee_Occupies_Office; package Office.Employee_Occupant is -- Office.Employee_Occupant.That_Of function That_Of ( The_Office : in Office.Object'Class ) return Employee.Pointer; -- Office.Employee_Occupant.Associate procedure Associate ( The_Office : in out Office.Object'Class; New_Employee : in Employee.Pointer ); -- Office.Employee_Occupant.Dissociate procedure Dissociate ( The_Employee : in out Employee.Object'Class ); end Office.Employee_Occupant; ---------------------------------------------------------------------- package body Office.Employee_Occupant is -- Office.Employee_Occupant.That_Of function That_Of ( The_Office : in Office.Object'Class ) return Employee.Pointer is begin Employee_Occupies_Office.Former_Of (The_Latter => The_Office'Access); end That_Of; -- Office.Employee_Occupant.Associate procedure Associate ( The_Office : in out Office.Object'Class; New_Employee : in Employee.Pointer ) is begin Employee_Occupies_Office.Associate ( The_Latter => The_Office'Access, The_Former => New_Employee ); end Associate; -- Office.Employee_Occupant.Dissociate procedure Dissociate ( The_Office : in out Office.Object'Class ) is begin Employee_Occupies_Office.Dissociate (The_Latter => The_Office'Access); end Dissociate; end Office.Employee_Occupant; ---------------------------------------------------------------------- I guess the real philosophical issue that everything is hinging on here is: "Is a binary association *part* of the abstraction of each of the classes, or is it a *separate* abstraction of its own?" Adam Beneschan objected to other solutions that put the association structure in a common root package, on the grounds that nothing from the "real world" was being modeled by that. I disagree with that, and agree with Magnus Kempe that in fact the association should be treated as a first-class abstraction of its own, in some fashion. However, I do not agree with Magnus Kempe that those other solutions "perfectly" implement this abstraction, because of the coupling that I see them introducing. If you want to avoid that coupling, then I think that two child packages hanging off of the associated root class packages, and/or a separate package entirely, would be better solutions. But a corollary to this issue is the question: "Do association operations *need* to be primitives of each of the associated classes?" I'm beginning to think not, but I'm not sure I have a good handle on this question yet. Any thoughts? > As sketched, this provides a clean, clear interface. However there >are two problems. First, there is going to have to be some >abstraction breaking to implement the bodies of these units. It can >be done in a type safe manner, with private child units, and there are >other possibilities. I'm not sure what you mean by "abstraction breaking". Is there some trick we could use to get those pointers into the tagged records after all? Perhaps "opaque" forms of the pointers that somehow get converted to "transparent" forms later? > Second, these functions are not inheritable, so you might want: > > with Offices; use Offices; > function Employees.Assigned_Office(E: in Employee'CLASS) > return Office'CLASS; > > with Employees; use Employees; > function Offices.Occupant(O: in Office'CLASS) return Employee'CLASS; > > instead of the versions above. Yes, I believe I drew the same conclusion early on, that these would be class-wide, non-dispatching operations, even if they could somehow be placed in the root package. I think it's reasonable to assume that a Rumbaugh-style association should apply equivalently to all instances of a class and its subclasses >Since such subprograms would have >to deal with all of the (possibly) many subclasses to employees or >offices, this approach can result in unmaintainable code. However for >small projects, or for attributes which are orthogonal to the rest of >the abstraction, this approach works just fine. I don't think there's any problem here. It seems to me the most reasonable interpretation of a Rumbaugh-style Object Model would not allow subclasses to alter the nature of an association (at least, in structural terms) which was established by a superclass. If we were tempted to do so, then I think we'd have to question the quality of our analysis model (e.g., "does this association really apply to this whole superclass?"). I'd say in general that each association, at least in terms of structure, *would* be completely orthogonal to other parts of the abstraction of a class, including the other associations and value-oriented attributes. If so, then it's perfectly natural to deal with each association one at a time, whether you do so in child units or separate association packages. > If any extension to the current draft standard is required in this >area--and I don't think one is-- Nor do I, really. (Even despite my post on "package abstracts" -- that was more of a thought experiment, not a serious recommendation or proposal.) My belief is that Ada9X is quite expressive enough, all we need to do is find creative combinations of the facilities that are already there. >it would be to change 3.2.3(6) to make >child units eligible as primitive operations, probably under the >influence of a pragma. I certainly don't recommend such a change to >the standard--it would open up all sorts of holes--but that should >prevent someone from experimenting with such a pragma. Certainly would be an interesting experiment, but dangerous. (Wear protective gear. ;-) The difficulty I see is that the primitive interface of a tagged type (embodied in its tag, perhaps implemented as a method jump table) would be a fluid thing, dependant on how many child units were actually compiled and "withed" within a program. It seems that a compiler alone would not be able to resolve this, and that it would have to be a function of a linker. >-- > Robert I. Eachus >with Standard_Disclaimer; >use Standard_Disclaimer; >function Message (Text: in Clever_Ideas) return Better_Ideas is... -- John Volan -------------------------------------------------------------------------------- -- Me : Person := (Name => "John Volan", -- Company => "Raytheon Missile Systems Division", -- E_Mail_Address => "jgv@swl.msd.ray.com", -- Affiliation => "Enthusiastic member of Team Ada!", -- Humorous_Disclaimer => "These opinions are undefined " & -- "by my employer and therefore " & -- "any use of them would be " & -- "totally erroneous."); --------------------------------------------------------------------------------