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.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,e5eb8ca5dcea2827 X-Google-Attributes: gid103376,public From: Matthew Heaney Subject: Re: Ada OO Mechanism Date: 1999/06/05 Message-ID: X-Deja-AN: 486094078 References: <7i05aq$rgl$1@news.orbitworld.net> <7i17gj$1u1k@news2.newsguy.com> <7icgkg$k4q$1@nnrp1.deja.com> <3749E9EC.2842436A@aasaa.ofe.org> <7id2eo$fag@drn.newsguy.com> <3749FF7D.F17CE16A@aasaa.ofe.org> <374AC676.F7AE0772@lmco.com> <7ieuja$5v9@news1.newsguy.com> <7ifd6l$bmf@sjx-ixn1.ix.netcom.com> <7ihf6i$4hv@dfw-ixnews10.ix.netcom.com> <7ii2bq$5nk@sjx-ixn6.ix.netcom.com> NNTP-Posting-Date: Sat, 05 Jun 1999 12:19:53 PDT Newsgroups: comp.lang.ada Date: 1999-06-05T00:00:00+00:00 List-Id: Richard D Riehle writes: > Now for the bad news. Ada access parameters allow the programmer > to violate the encapsulation too. Although the access parameter > itself is regarded as an in parameter in Ada, No. An access parameter is an in out param, that automatically dereferences a pointer. An access parameter is used to implement by-reference abstractions: type T (<>) is tagged limited private; procedure Op (O : access T); Singletons that are instances of a type are written this way: package Mazes.Games is type Maze_Type (<>) is limited private; type Maze_Access is access all Maze_Type; function Maze return Maze_Access; function Get_Room (Maze : access Maze_Type; Number : in Room_Number) return Room_Access; ... end Mazes.Games; So a client would just say: Room : constant Room_Access := Get_Room (Maze, 2); ^^^^ Access parameters give Ada reference-semantics, without the syntactic overhead (having to say .all everywhere). There are many examples of this idiom in the ACM patterns archive: > a programmer may dereference data being referenced, if there is > visibility to the data. Yes, but in the current version of Ada, an access parameter is really an in out parameter. You are correct in pointing out that there are times when an operation doesn't need to modify the state of the object, even though it takes an access parameter. But I don't buy the argument that this makes all kinds of abuse possible. A client can only manipulate an abstraction using the (primitive) operations provided. And the writer of an abstraction has visibility to the implementation anyway. He's not going to suddenly go berserk, arbitrarily changing state, just because one of the operations happens to take an access param instead of an access constant param. > In this regard, I think C++ is a little stronger because of the > option of const functions and const parameters. My preference for a > future addition to the Ada standard would be some kind of constant > function such as, > > constant function F (A : access some-type) return T; > > or, closer to C++, > > function F (A : constant access some-time) return T; > > which would disallow any monkey business with the function. The more likely scenario will be: procedure Op (O : access constant T); function Op (O : access constant T) return Item; This was considered for Ada95 but didn't make the cut, because the language designers were trying to keep the number of language changes small. In this respect, C++ is superior to Ada95, but not for the reasons you argue. > In fact, this would correspond to the notion of a query with no > possible side-effects (much like the protected type function) and > satisfy Bertrand Meyer's insistence that queries should only query > and modifiers should only modify. Then I disagree with Bertrand (what else is new). From a specification point of view, a selector might not be state-changing, but there may be very good reasons to make an internal state-change. For example, one might wish to record how many times a certain function was invoked. "Casting away const" is a useful, and necessary, feature. > This is one feature of Ada, for object-oriented programming where > I think C++ is a little stronger. The problem with Ada is that it doesn't separate interface from implementation enough. The parameter modes that appear in a spec apply to the body too - but why? For example, there's no easy way to write the function I mentioned above: function Get_Item (O : in T) return Item_Type; Now how am I supposed to record that Get_Item got invoked? T has mode in, not in out, so I can't easily change O's state. Suppose I had in out function params: function Get_Item (O : in out T) return Item_Type; This would solve my problem, but it's still advertising more than I really wish to advertise. Why does the client care that O is in out? (He doesn't.) I only needed to state that in the spec, because it's what I need to do in the body. > >It is a fallacy that friend functions break encapsulation. Friendship > >is granted explicitly by a class. These friends are part of the > >encapsulation. > > Friends are "part of the encapsulation", but also seem, to me, to be > an awkward feature included to compensate for problems associated > with the class <=> type <=> module model. Without this kind > of model, the notion of friends would be unnecessary. Yes. You need friend because the type and mode were coupled. In Ada, visibility is controlled by the module feature, which is separate from the type feature, which means a friend feature isn't necessary. The "bad" thing about the friend construct is not that it violates information hiding; after all, child packages and co-declarations allow you to "violate" information hiding too. The issue is that you can't extend visibility from one abstraction to another without paying a compilation penalty. > >Mutable has nothing at all to do with this - mutable > >members of an object may be modified even when the object is declared > >to be 'const'. The intent is to provide a difference between logical > >constness and physical constness. For example, one would expect that > >the characters of a const string would never change, but there could > >be an internal reference count that needs adjustment. That count would > >be declared mutable. > > I understand that mutable is a feature for reversing const'ness. We > have in Ada the notion of a general access constant that satisfies > the situation you mention, if I understand your explanation. It just > seems, once again, that this is an add-on to compensate for a language > weakness rather than a coherent part of the overall design. Don't agree. This is a feature we don't have in Ada, and it is sorely missed. What we have to do now, in order to create a physical state-change in an object during an operation that is logically constant is: 1) make sure the type is a by-reference type, per RM95 6.2 (4) 2) take its address, per RM95 13.3 (16) 3) convert the address to an access-to-variable, per RM95 13.7.2 This is a royal pain in the ass. Worse, by having to go through an intermediate Address, we throw all type-checking out the window, which makes errors all the more likely. In effect, it reduces to a C program. Thankfully, gnat has 'Unrestricted_Access, which preserves all type checking, but this feature in non-portable. An example is the implementation of function Random: function Random (G : in Generator) return Uniformly_Distributed; See the files (especially the Implementation Note) a-nudira.ad? a-nuflra.ad? in the gnat sources for an example. There are many example of using Address_To_Access_Conversions in the ACM patterns archive, to get around these limitations in the language. > >For completeness sake, class member templates *can* be used to break > >encapsulation. The C++ philosophy is to protect encapsulation against > >Murphy but not Machiavelli. The same is true in Ada95, via the child mechanism. You need a way to efficiently extend an abstraction, and this often requires extending visibility to the implementation. Both languages have satisfied the need, but by using different mechanisms. The benefit of the Ada way is that there is no compilation penalty.