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.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,baa6871d466e5af9 X-Google-Attributes: gid103376,public From: "John G. Volan" Subject: Re: AQ&S Guidance on pragma Elaborate_Body Date: 1997/05/01 Message-ID: <33698E5C.22AA@sprintmail.com> X-Deja-AN: 238831018 References: <528878564wnr@diphi.demon.co.uk> <5jabeq$3ltk@info4.rus.uni-stuttgart.de> <5jfukp$lda@top.mitre.org> <33660F4B.1B45@sprintmail.com> Organization: Sprint Internet Passport Reply-To: johnvolan@sprintmail.com Newsgroups: comp.lang.ada Date: 1997-05-01T00:00:00+00:00 List-Id: Matthew Heaney wrote: > > In article <33660F4B.1B45@sprintmail.com>, johnvolan@sprintmail.com wrote: [snip] > >Of course, if it's just "a pair of abstract data types that are > >intimately related", and that's all there is to it, then the cost of > >abstraction-loss is relatively small, so co-encapsulation may be a > >reasonable option. > > That's all I was saying: a pair of ADTs that are intimately related. > Here's an example. In order to efficiently implement an active iterator, > that iterator has to have knowledge of the representation of the data > structure. [snip bounded stack & iterator code] > I'm certainly not advocating large packages. It's just that very often, > there are a small number of types (read: 2) that need to know about each > other's representation, or that one needs the know the represenation of the > other (the iterator above). > > This is just the concept of a friend in C++: to implement that in Ada, you > put the types together in the same package. No big deal. Um, sort of, but not exactly. Friendship isn't symmetrical: your friends can see your internals, but you can't see theirs (unless they grant you friendship in turn). Whereas co-encapsulation doesn't distinguish either party. The closest analog to friends in Ada95 is child packages -- but that's not an exact analogy either. More on this below... > There is NO loss of abstraction when the stack and its iterator are > co-located in the same package; There might not be any loss of abstraction from the point of view of some third-party client that uses both abstractions, but there _is_ a loss of abstraction _between_ the two types. For an isolated case like this, I admit the harm is small, and may be outweighed by other design issues ("declaration span", as you put it). But the fact remains that, once you choose to co-encapsulate them, the stack type and the iterator type are under no obligation to treat _each other_ as abstractions. > the abstractions were meant to be used together. Oh really? Always? In every conceivable application? What if there's an application that doesn't need to use stack iterators, but only needs some basic operations that work at the "whole-stack" level (e.g., push, pop)? Then the iterator stuff becomes extraneous. As you point out yourself, the relationship here is really just one-way: The iterator type needs to know about the implementation of the stack type, but the stack type can be implemented quite nicely without any notion of stack iterators. Rather than thinking of stacks and their iterators as two inseparable parts of a single abstraction, you could view stack iterators as an extension on the abstraction of stacks. This suggests that iterators might be better off in a _child_ package. The iterator type could still see the implementation details of the stack type, but the original stack package wouldn't be "cluttered" with this extra concept of an iterator. This way, iterators are optional: If an application needs them, it can "with" the child package; if it doesn't need them, it won't have to pay the cost of having the iterator code linked in. A child package is something like a friend, but not quite: A child can be declared after the fact without modifying the original parent package. The parent package doesn't have to mention the child (and essentially _can't_). But to become a friend of class X, this has to be mentioned in advance as part of the declaration of class X. If you decide you want to confer friendship later, you must modify your original class to do so. > I don't subscribe to these gloom and doom scenarios about > putting types together; this is how the language was _intended_ to be used. Hey, it's a general purpose language with a lot of features. It was "intended" to support a _variety_ of coding styles and idioms. > And it's certainly true that this affects understandability: it makes it > MORE understandable when the "declaration span" is minimized. But size and complexity also impact understandability: If you designate something as a "single indivisible unit of abstraction", the larger that unit happens to be, and the more pieces it contains, the more a user must digest, and the harder it is to understand in one sitting. Breaking things out into child packages allows the users take things in stages: They can look at the stack package first, in isolation, and understand how things work at the "whole-stack" level of granularity. Then later they can look at the iterator child package and tackle the concept of iterating over the stack at the finer, item-level of granularity. > I don't understand why a client needs to understand "internally" the > Bounded_Stack and its iterator to use it: If readers are presented with a package containing two abstract data types, then they are faced with the prospect of understanding the semantics of both ADTs together. If the package is well-documented, then the documentation will fully describe the outwardly-visible behavior of two ADTs, including how they might interact with each other. If the package is poorly-documented (as is so often the case), then readers will need to explore the package body, looking in detail at the code for both ADTs, before they can be satisfied they understand what either ADT is really all about. Documentation sometimes lies, or is obsolete, so users may need to pore over the body code anyway. Eventually, they might realize, for instance, that one of the ADTs is actually entirely independent of the other, but it may take some mental grinding before they can grok that. > This is > analogous to using a declare block to move declaration of an object as > close as possible to its use. I don't get this analogy at all -- in fact, I think it's more apt as an analogy for the _opposite_ position: You have a declaration X that is only needed in a local scope, so instead of declaring it in a more global scope along with Y, Z, and W (which might be somewhat related to X but are used more globally), you isolate X in its own a declare block. Similarly, you have an abstract data type X that you only need for special purposes under special circumstances, so instead of lumping it into the same package with Y, Z, and W (which might be somewhat related X but are used more generally), you isolate X in its own package (which might be a child package). ------------------------------------------------------------------------ Internet.Usenet.Put_Signature (Name => "John G. Volan", Home_Email => "johnvolan@sprintmail.com", Slogan => "Ada95: The World's *FIRST* International-Standard OOPL", Disclaimer => "These opinions were mever defined, so using them " & "would be erroneous...or is that just nondeterministic now? :-) "); ------------------------------------------------------------------------