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,93fa00d728cc528e X-Google-Attributes: gid1108a1,public X-Google-Thread: 103376,93fa00d728cc528e X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-10-27 12:03:41 PST Path: nntp.gmd.de!xlink.net!howland.reston.ans.net!sol.ctr.columbia.edu!news.kei.com!eff!blanket.mitre.org!linus.mitre.org!linus!mbunix!eachus From: eachus@spectre.mitre.org (Robert I. Eachus) Newsgroups: comp.lang.ada,comp.object Subject: Re: Generic association example (was Re: Mutual Recursion Challenge) Date: 27 Oct 94 10:53:37 Organization: The Mitre Corp., Bedford, MA. Message-ID: References: <1994Oct18.221751.15457@swlvx2.msd.ray.com> <38289r$79m@oahu.cs.ucla.edu> <1994Oct19.143843.372@wdl.loral.com> <38fi4r$l81@oahu.cs.ucla.edu> <1994Oct24.203214.4967@swlvx2.msd.ray.com> <1994Oct26.232154.29094@swlvx2.msd.ray.com> NNTP-Posting-Host: spectre.mitre.org In-reply-to: jgv@swl.msd.ray.com's message of Wed, 26 Oct 1994 23:21:54 GMT Xref: nntp.gmd.de comp.lang.ada:16239 comp.object:16691 Date: 1994-10-27T10:53:37+00:00 List-Id: (I'll post one last time on this subject here, then take any follow-up to mail.) In article <1994Oct26.232154.29094@swlvx2.msd.ray.com> jgv@swl.msd.ray.com (John Volan) writes: > I suppose we could apply a pragma Suppress at some point to > eliminate the extra dynamic check, once the program was fully > tested. But, more to the point, we've lost a certain amount of > expressibility. Since the Ada code doesn't directly express what > we mean, its readability by other Ada programmers (for example, > maintainers) is reduced. Let the compiler do the work. If there is only one concrete type in an abstract class, the check might result in a call at run-time to a check, but the check should be implemented as a return instruction. If there are types derived from Office or Person, there may be an actual check in some other cases that you want made. But any compiler that does run time checks for abstract types, or builds dispatch tables for abstract types has a bug. (There are cases where you may have a view of an object as an abstract type, but that must always be a static view--at least, that is the intent.) > What cannot be easily named, cannot be easily explained, and > therefore cannot be easily understood. The style of Ada 9X programming I am focusing on addresses this by making that disappear from sight. The only objects visible in the "main" program should be complete objects of complete types. The rest should be treated like making sausage or scrapple and hidden in private parts whereever possible. > But that's not the only way you could arrange things. If you > noticed, in my previous posts, I actually conceived of > *distributing* this "Setting" responsibility to *both* of the > classes. In other words, I imagined having *two* "Associate" > operations: one that was primitive for an Employee and one that > was primitive for an Office. The Associate for Employee would > only directly modify the Employee's pointer to its Office; > likewise the Associate for Office would only directly modify the > Office's pointer to its Employee. That follows the "Law of > Demeter": an object's operations should only directly manipulate > its own attributes, but not the attributes of any other object. > However, both of these Associate operations would also be > responsible for satisfying the invariant of the association: i.e, > if an Employee points to an Office, then that Office must point > back to that Employee as well. The most convenient way for the > two Associate operations to do that would be ... to call each > other! In other words, they would be *mutually recursive* > subprograms, reflecting the *mutually recursive* relationship > between the two classes. Excellent reasoning, but I wouldn't code the blasted bodies that way. Infinite recursion is frowned upon, so if set for office calls set for person and vice-versa, something has to break the loop. The "best" way I found was to set the local attribute, then check the remote attribute and if it was "wrong" call the matching set. It works, but it is pretty kludgy. I'm willing to pay a bit for elegance, but that's a little expensive. Above you talk about eliminating redundant checks, here you not only prohibit simple operations from being effectively inlined, but require a check that the thing you just set has the right value. According to ME ;-) the association package "owns" the Attribute fields and is the only one to monkey with them, and this is the right and proper approach in Ada 9X. It is different, but it seems better from an encapsulation point of view. With the Smalltalk or C++ model large and complex object implementations soon get buried in interactions between different components of an object. In this model, each abstraction takes care of its own. I left some stuff out here for simplicity, but in a "real" implementation of this idea, I would insist that both object be derived from Controlled, or Limited Controlled, and I would insure that there were no dangling pointers when an object was destroyed, and that copying an object did not copy the assignments. (In a many-to-one or many-to-many implementation the policy would be different of course.) In other words, fix all those bugs before they occur, and transparent to the user of the abstraction. > You see, that's what I mean -- you've lost the ability to express > the exact type of the other object in a static fashion, even > though you, as the designer, do know what that exact class should > be, a priori. In the instance you can use subtypes and renames to express things right, and in a real world application, as I said I would put as much of this as possible in the private part. (Due to the form of the generic, the outer instance must be public, but that's about it--other than the operations and types you want to show.) > Interesting interpretation. I didn't think there was any trouble > with returning "null". Clients could just interpret a "null" > value as meaning that E isn't associated with any Target. But > that's okay -- you just have a slightly different abstraction in > mind than I have. Major difference, but subtle in its way. I hid the "pointer" types, so all external interfaces deal with objects, not pointers to objects. Yes, we know that what gets passed around is a reference, so there is no extra calling overhead, but doing it this way eliminates--if done correctly--dangling pointer worries for users of the abstractions. I didn't show all of that, but for example, the finalization operation on a Person would unset his office. So there is no null value to return. > > procedure UnSet(E: in out Extended'CLASS); > > procedure UnSet(IE: in out Extended'CLASS); > > -- UnSet the attribute. If already set, unset the partner as well. > By the way, in the case of "many"-cardinality, I think UnSet would > actually wind up being a single operation with two parameters. (Read a little closer. UnSet can always get the partner when needed, and the code does it that way. But, yes, you also need the two parameter form in the many-to-many mapping to remove exactly one pairing.) -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is...