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.3 required=5.0 tests=BAYES_00,INVALID_MSGID 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: matthew_heaney@acm.org (Matthew Heaney) Subject: Re: Mutually dependent private types Date: 1998/05/22 Message-ID: #1/1 X-Deja-AN: 355720841 Content-Transfer-Encoding: 8bit References: <6k25ra$6j7$1@nnrp1.dejanews.com> <3565B105.9BFB4788@ac3i.dseg.ti.com> Content-Type: text/plain; charset=ISO-8859-1 Organization: Network Intensive Mime-Version: 1.0 Newsgroups: comp.lang.ada Date: 1998-05-22T00:00:00+00:00 List-Id: In article <3565B105.9BFB4788@ac3i.dseg.ti.com>, John Volan wrote: >> 3) The mutual dependency in operations is effected by making the other type >> have type Root'Class, in the spec. In the body, just downcast the object >> to the representation type. Of course, the compiler can't verify >> statically that the object is really of the required type, but you'll find >> out at runtime, because a tag check happens when you downcast. > >See http://bluemarble.net/~jvolan/WithingProblem/FAQ.html#strong_typing, >where I describe how the with-ing problem can be avoided, at the expense >of Ada95's static strong typing checks, by deriving all your object >classes from some universal root type (Objects.Object_Type was my >suggestion). Yes, but I don't agree that all tagged types must derive from the _same_ root type. Each hierarchy can have a different root type. >> This is out-of-the-box Ada 95. No complex language extensions or >> Unchecked_Conversion or implementing types using access types is required. > >Yes, but unfortunately it forces Ada95 to act a lot like Smalltalk. But only because you derive every tagged type from the same root type (a la Smalltalk). In that case, you really are throwing _all_ static type checks out the window. There is no need for that. Simply create a different root type for each hierarchy. >The >programmer presumably knows statically that O1 is always supposed to be >a T1 and O2 is always supposed to be a T2, but this coding style >prevents the compiler from knowing that. It is very possible that >someone could write code that tried to pass something other than a T2 >into P1.Op1. If you use different type hierarchies, then the client could only accidently pass types in the T2 hierarchy. But this is very unlikely, for example package T2_Roots is type NO_ONE_DERIVE_FROM_THIS_EXCEPT_P2_T2 is abstract tagged null record; end; with T2_Roots; package P2 is type T2 is new T2_Roots.NO_ONE_DERIVE_FROM_THIS_EXCEPT_P2_T2 with private; ... end; Hopefully it will be obvious that package T2_Roots is only for use by P2! Of course, if, during a code walk-through, you see this with T2_Roots; package P3 is type T is new T2_Roots.NO_ONE_DERIVE_FROM_THIS_EXCEPT_P2_T2 with private; ... end; then you have bigger problems than mere language omissions. >Such code would of course be a bug, but it wouldn't be >caught until the call to P1.Op1 was actually executed and the tag-check >was performed. Depending on the testing scheme used during development, >this call may or may not get executed. So this bug could actually go >undetected during testing and wind up being reported by an irate end >user. There are constraint checks for all additions, subtractions, etc. There are index checks for array dereferences. There are storage checks during allocation. etc. One extra tag check isn't going to make a difference (especially since you could suppress the check, knowing that ONLY P2.T2 derives from T2_Root). Remember, the chain of mutual dependencies only needs to be broken in one place. Adam's problem could have been solved simply as with T2_Roots; package P1 is type T1 is private; subtype T2 is T2_Roots.NO_ONE_DERIVE_FROM_THIS_EXCEPT_P2_T2'Class; procedure Op1 (O1 : T1; O2 : T2); ... end; with T2_Roots; with P1; package P2 is type T2 is new T2_Roots.NO_ONE_DERIVE_FROM_THIS_EXCEPT_P2_T2; procedure Op2 (O2 : T2; O1 : P1.T1); ... end; Only one type (here, T2) needs to be split into a root & derived type pair. >> I read John's paper, but I don't find his argument convincing. Doesn't the >> following code solve the putative with'ing "problem"? > >[snip Matt's example] > >The *_Rep types aren't even necessary, if you're going to go with the >universal root type strategy: > >package Adam is > type Root is abstract tagged null record; >end Adam; I argue that this is going too far. Each type can have it's own root type, as in package T1_Roots is type T1_Root is abstract tagged null record; end; with T1_Roots; package P1 is type T1 is new T1_Roots.T1_Root with private; ... end; package T2_Roots is type T2_Root is abstract tagged null record; end; with T2_Roots; package P2 is type T2 is new T2_Roots.T2_Root with private; ... end; Furthermore, this split is only required when there's mutual dependency. Most types are definately not mutually dependent, and so this workaround should only seldom be necessary.