From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.5-pre1 (2020-06-20) on ip-172-31-74-118.ec2.internal X-Spam-Level: X-Spam-Status: No, score=-1.9 required=3.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.5-pre1 Date: 10 Jul 91 22:06:45 GMT From: eachus@mitre-bedford.arpa (Robert I. Eachus) Subject: Re: Mutual distrust Message-ID: List-Id: In article <6883@ns-mx.uiowa.edu> jones@pyrite.cs.uiowa.edu (Douglas W. Jones,2 01H MLH,3193350740,3193382879) writes: Here's a problem I've come up against in designing a simulation program voluntarily written in Ada... The problem: Each PLACE must have an associated set of people who occupy that PLACE, and each PERSON must know what place he (or she) is in and what other people occupy that same place. This implies a cyclic relationship between the packages GEOGRAPHY and POPULATION. The one simplifying factor is that there is only one place in which it is necessary for the PLACE occupied by a PERSON to be known, and that is inside the action routines applied to that particular PERSON, which are inside POPULATION. Here are the possible solutions I thought up: 1) Add a third package -- something of a relational package that associates people with places, so that given a PLACE it can supply the set of all people there, and given a PERSON, it can tell what PLACE that person occupies. This has the advantage of allowing GEOGRAPHY and POPULATION to be completely independant, but because PERSON and PLACE are private types, there is no way I can think of to efficiently implement this third package. -- There is an easy way to do this using the Tucker Taft amendment, -- which allows an incomplete type declaration in a private part to be -- completed in the body of the package... package POPULATION is type PERSON is private; -- operrations on PERSONs not explicitly involving PLACEs. ... private type PERSON_RECORD; type PERSON is access PERSON_RECORD; end POPULATION; package GEOGRAPHY is type PLACE is private; -- operations on PLACEs not explicitly involving people. ... private type PLACE_RECORD is private; type PLACE is access PLACE_RECORD; end GEOGRAPHY; with POPULATION; with GEOGRAPHY; package PEOPLE_AND_PLACES is -- operations involving both people and places... end PEOPLE_AND_PLACES; with POPULATION; with GEOGRAPHY; package HIDDEN is type REAL_PERSON is record... type REAL_PLACE is record... function GET_REAL(P: in POPULATION.PERSON) return REAL_PERSON; function GET_REAL(P: in GEOGRAPHY.PLACE) return REAL_PLACE; end HIDDEN; with UNCHECKED_CONVERSION; package body HIDDEN is type PERSON_POINTER is access REAL_PERSON; function CONVERT is new UNCHECKED_CONVERSION (POPULATION.PERSON, PERSON_POINTER); function GET_REAL(P: in POPULATION.PERSON) return REAL_PERSON is begin return CONVERT(P); end GET_REAL; -- do the same for PLACEs ... end HIDDEN; with HIDDEN; package body GEOGRAPHY is type PLACE_RECORD is new HIDDEN.REAL_PLACE; -- Now you can see the actual record declarations in POPULATION, -- GEOGRAPHY, and PEOPLE_AND_PLACES (assuming that these bodies and -- only these bodies say with HIDDEN. Revising either of the record -- structures will require recompiling the bodies of all three -- packages but you would have to recomile two of them anyway. -- However, the only specification which requires recompilation is -- that for HIDDEN. -- I usually only advocate packages like HIDDEN as a way of -- minimizing recompilations, but sometimes it has other advantages. 2) Take advantage of the simplifying factor, which allows the following package dependancy to be established... This is the solution I ended up using, but I don't like the assymetry of it, and I don't like what I have to do to it to allow OCCUPANT to return a set of people instead of a single PERSON. -- What's the problem? Have two functions one, call it COUNT, -- returns the number of residents and the other, OCCUPANTS, returns an -- array of PERSONs. The loop which processes this may have to use a -- different trick... Let's say that POPULATION.PEOPLE is an array -- (POSITIVE range <>) of PERSON. loop SOMEPLACE := ... -- get next location declare SOMEPEOPLE: constant POPULATION.PEOPLE := OCCUPANTS(SOMEPLACE); begin for I in SOMEPEOPLE'RANGE loop PROCESS(SOMEPEOPLE(I)); end loop; end; end loop; -- The constant keyword is the magic cookie which allows you to -- declare SOMEPEOPLE without supplying the range. Since PERSON is an -- access type, PROCESS is allowed to change fields of the various -- records but not to assign a new access value to SOMEPEOPLE(I). -- Robert I. Eachus with STANDARD_DISCLAIMER; use STANDARD_DISCLAIMER; function MESSAGE (TEXT: in CLEVER_IDEAS) return BETTER_IDEAS is...