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,FREEMAIL_FROM, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,fa2cc518ef3b992c X-Google-Attributes: gid103376,public From: "Vladimir Olensky" Subject: Re: tagged types extensions - language design question Date: 2000/02/01 Message-ID: X-Deja-AN: 580455108 References: <3895EC61.9B2157A8@rational.com> Organization: Posted via Supernews, http://www.supernews.com X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3 Newsgroups: comp.lang.ada X-Complaints-To: newsabuse@supernews.com Date: 2000-02-01T00:00:00+00:00 List-Id: Mark Lundquist wrote in message <3895EC61.9B2157A8@rational.com>... >Vladimir Olensky wrote: > >> The question is : >> >> Why in Ada it is not allowed to extend both public >> and private part of the tagged type at one step > >Good question! > >None of the heavy hitters (Tucker et al) have weighed in, so let me >venture my guess... I'd say the bottom line is, there isn't any really >good reason. That's the short answer, stay with me if you want the >details... :-) First of all, thanks for your very interesting response. I've got real pleasure to follow your considerations. Sometimes question that popped out impulsively because your eyes were caught by something that seems unusual leads to very interesting results. <...> >The way of thinking that's "natural" to Ada is to think of the derived >type as providing an extension to the parent type, which extension >is either public or private. I always try not to put myself into the prison of any particular way of thinking. It helps to avoid becoming slave of some particular "club" and be open for anything that might be interesting which is outside the area covered by some particular way of thinking and also to avoid thinking "inertia". In this respect any programming language is just some tool to do some job. Ada is not exception from that rule. And as any other tool each one has it's advantages and drawbacks. Any master should not become the slave of any particular tool. He should be the master who knows what is the best way to use each of them. Tools should be the slaves of the master. Of course any master has the tool that he likes most of all and that's fine. <...> >So I think the real question is not, why can't you extend publicly and >privately "all at once", but rather, why not let records and extensions >have private components? It seems clear that any compromise of >3.9.1(1) is a very slippery slope that shoots you straight down to this. >As I said before, I don't see any compelling reason why this couldn't >have or shouldn't be done. Is there a compelling reason to do it (or to >have done it)? I don't know. You could probably make a pretty good >case for it. This twist is very interesting and exciting and I think it could be elaborated even further. We may ask several further questions that lead us very close to some notion that will be discussed a little bit later: 1. What if in some child package we will want to have parent public fields to be private and some parent private fields (not all of them) to be public. And having some declarations in the parent package we stick to them forever. There of course exists workaround but it is not very clean. 2. May be it is better to have most of the parent fields to be private and have the ability make some of them publicly available when needed ? These questions are not new and differently addressed in different existing languages. In C++ we have class access modifiers that allows to make public fields to be protected or private in the descendant classes. But this solves only one part of the above problem. First of all these modifies are applied to the whole class and not to the particular field. So if I use private or protected modifier in the derived class all the parent public members become private and protected in the child class. On the other hand there is no way to make some private parent components to be public in derived classes. C++ notion of friend does not help much in this issue. So C++ approach is not flexible at all regarding this issue. Another approach was taken in Modula-3 by introducing opaque types. They were discussed several times here in CLA and several times I gave a reference to very interesting article which discuss M3 opaque types. This is "Partial Revelation and Modula-3" at: http://www.research.digital.com/SRC/modula-3/html/partial-rev/index.html Most interesting thing about M3 opaque type is not the fact that their properties could be completely hidden in the implementation module. Some people tend to think that this is the main advantage of the opaque types but in fact it is not. This kind of property could be easily emulated in C++ and Ada. The main virtue of opaque types lies in their ability to reveal some of their hidden properties when needed - so called "partial revelation". This allows to have parent type to be completely hidden from the client and any descendant to reveal only those parent properties that needed for particular interface to the given implementation. Here again we come up to some more general design issues. For quite some time there exists the notion of the "Black Box" design approach that becomes more and more popular. Oberon even called their new product as "Black Box" framework. There are two approaches to the "Black Box" design model. 1. One is that we first create "Black Box" interface that describes it's functionality and then we implement that functionality in a way hidden from the client. In this model "Black Box" is the implementation of the given interface. This is approach that followed by the most programming languages that support the "Black Box" design approach in a way that allows to hide "Black Box" functionality implementation from the client. Ada, Java, C++, Modula-2 fall more or less in this category. 2. There exists another approach which was used so far more in hardware design than in software design and which is more close to real life model. Using this approach the main focus is the "Black Box" itself and it's functionality. "Black Box" is something that provides some kind of universal functionality in some domain which in turn could be provided by the set of some more small "Black Boxes". This functionality could be used by completely different clients that may need different interfaces to that functionality. So here we come to the point where we need to define more then one interface (for different clients) to the given "Black Box". There are not many languages that support directly this concept, but Modula-3 is among them. This is the area where Modular-3 opaque types and partial revelation shine most of all. This is exactly what is needed to support the second design model. In Modula-3 we can create "Black Box" component and define multiple interfaces for different clients using opaque types and partial revelation. I tend to think that Modular-3 designers were at least ten years ahead of their time regarding this issue. But who knows may be will see reincarnation of this idea pretty soon in some other real life language. May be in Ada , who knows :-). Though Ada does not support directly exporting several interfaces to the same implementations this could be partially done via child packages. The only question is how to reveal hidden/private details. As shown below this could be done using existing Ada syntax as well. The only one thing which could not be expressed using existing syntax is the partial type revelation. Namely this is ability to show in one interface that exported type is a descendant of some parent type and in another interface to show that it also has properties of another type. Some people may argue that the last approach does not differ much from the first one and even that the first one is more close to the real life development process. I can answer to this: Look around. Among all devices and appliances that are around us we see mostly the second model and not the first one. Not some boxes are built around their interfaces. Quite the contrary - all the interfaces are built to provide access to box functionality. Just look at a simple PC box which implements some general kind of functionality and provides many different interfaces to this functionality for different clients. It is interesting to note that CORBA and COM/DCOM technologies also tend to support more the second model. Both of them allow to use multiple interfaces to the distributed "Black Box" component . And again here I see that opaque types and partial revelation perfectly fit to and support these two modern technologies. So elaboration of the answer to my original question leads us to very interesting area indeed. Another interesting topic related to this subject is object/class properties (design and run time) that we want to make available to the client. Any "Black Box" component may have some properties the state of which could be needed to be available to the client. There again exists several approaches. The one of them used in Delphi Object Pascal (DOP) is very close to Ada syntax. In DOP we can just declare something type T = class (some_parent_class) private function Get_Property1 () : Integer; function Get_Property2 () : Integer; procedure Set_Property1 (prop :Integer); procedure Set_Property2 (prop: Integer); public property prop_1 : Integer read Get_Property1 write Set_Property1; published property prop_2 : Integer read Get_Property2 write Set_Property2; end; Then having an instance of type T , we can use that attributes directly : program Var obj : T; p1, p2 : Integer; begin p1 := obj.Prop_1; p2 := obj.Prop_2; { or } obj.Prop_1 := 2; obj.Prop_2 := 10; end; In Ada it seems that we can do something like that by overloading ":=" operator. But we can not do that for distinct type but not for particular property instance/field. So this is not perfect solution. On the other hand I see that current Ada rep syntax perfectly fits here. We can just declare: package P is type T is tagged private; type T1 is new T with record -- public properties A and B; -- they are public views of some internal states -- which in turn could be fields of some complex -- "Black Box" internal structures; A : [property] constant Integer; B : [property] Integer; end record; private type T is tagged record < some fields> end record; function Get_Property1 (obj : in out T ) : Integer; function Get_Property2 (obj : in out T ) : Integer; procedure Set_Property2 ( obj : in out T; prop: Integer); -- and then rep clause: for T.A'Get use Get_Property1 (obj : in out T ) : Integer; for T.B'Get use Get_Property1 (obj : in out T ) : Integer; for T.B'Set use Set_Property1 ( obj : in out T; prop: Integer); end P; No need to introduce any new keywords. Ada allows rep clauses for fully visible types to be in the private area. May be only property keyword could be introduced for clarity but generally there is no need of it at all; Everything fit perfectly into current Ada syntax; This approach can also could be used to reveal some private details of parent type in a new interface using direct mapping: package N is type T is new P with record A: some_type; B: some_type; end T; private for T.A use P.something; for T.B use P.something_else; -- or using renaming clause: T.A renames P.something; T.B renames use P.something_else; end N; All above is just brief description of what comes to mind when starting to elaborate the answer to "innocent" at the fist glance question. Regards, Vladimir Olensky