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-Thread: 103376,6129ccd596d4814d X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!news.glorb.com!fu-berlin.de!uni-berlin.de!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: Abstract Operations On A Tagged Record Date: Sat, 30 Oct 2004 16:34:34 +0200 Organization: cbb software GmbH Message-ID: <972rdspp3zvm.brfeteze2k67.dlg@40tude.net> References: Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: news.uni-berlin.de f4y4P8KgGOIE6Iou/zupgQMIPZD3ZmWhpwNwP8yDkZaEniE5s= User-Agent: 40tude_Dialog/2.0.12.1 Xref: g2news1.google.com comp.lang.ada:5913 Date: 2004-10-30T16:34:34+02:00 List-Id: On Sat, 30 Oct 2004 13:04:37 GMT, Marin David Condic wrote: > Can someone clarify my understanding (perhaps again) on abstract > operations and the implications as a class heierarchy is being > constructed? Specifically, what I want is something like this: > > A base class has an abstract operation, but I may not yet know the data > types of the parameters - or possibly even the number of parameters. > Let's say what I want is like this: > > procedure Some_Op () is abstract ; The first question is what is the class you are talking about. When class refers a package to be extended by other packages etc, then it is far away from Ada, tagged types and not very clear at all. So I assume that class refers here types, and more specifically a hierarchy of types. If so, then Some_Op should be an operation of that type. > When I get to the child class, what I really need is: > > procedure Some_Op (Some_Param1 : in Integer; Some_Param2 : in Float) ; > > For a different child class I may want: > > procedure Some_Op (Some_Param1 : in Integer; Some_Param2 : in Character; > Some_Param3 : in Boolean) ; > > The base class wants to express the basic necessity of "Some_Op" but it > really doesn't know if it will have 1, 2 or N parameters or of what > type. But it *has* to have a parameter of its own type, the parameter of the class. Some_Op is an operation of the "class". This is how clients become aware of it. They (the clients) are in the class so they have this operation too. There are only two ways to have an operation defined on class: 1. class-wide: Some_Op (Object : in out T'Class); 2. primitive operation: Some_Op (Object : in out T); A primitive operation is defined on the class, but it is implemented "piecewise" by overriding (implementing) its parts in derived types. > My understanding is that if I express that as an abstract > operation, when I get to the child class, I'll have to supply a real > operation of the same name and same parameter profile. (Or can I use a > different parameter profile & satisfy the need for a concrete > implementation of the abstract op? My recollection is the compiler > whines about it or creates an overloading. Is this correct?) Yes, it is the case 2, so it is in fact one operation and thus it has one parameter profile. It is merely syntax sugar and an ability to call an override directly, without dispatch, that makes us believe that it has a "different" profile. A primitive operation is abstract when it is not implemented for some of the class types. It is safe to do so because these types will be then abstract and so will have no instances. But any concrete derived type should implement it for itself (by either overriding or inheriting from the [concrete] base). > I realize I can make another procedure "Some_Op" with a different > parameter list and that overloading will take care of me, but then I > didn't really need the abstract procedure in the first place, did I? It > becomes unnecessary baggage that you have to fill in just because its > there, but you have no intention of using it. Yes, because overloaded operations have nothing in common except the name. The idea of abstract primitive operation is to define an operation on the class. The class is needed only to write programs in terms of that class. One cannot write programs in terms of names without any semantics. For this there would be enough to have a text editor with cut/paste and substitute or a preprocessor (or C++ templates (:-)). > Do I understand this correctly? Is there away around it? Keep in mind > that what I want to express is the notion in a base class that Operation > X needs to be present, but the precise parameters to X will be > determined by the child class. Why this is needed? How this class might be used? Consider the most general case, I have an instance of some class member: procedure Foo (Object : in out T'Class) is begin X (Object, ?); Now I am going to apply X to Object. How do I know the parameter profile? There is no way to determine it. What cannot be used with class is not of the class. So X with varying parameter profile cannot be an operation of the class, unless you bring that variance under one roof. One way to do it is to pack all possible actual profiles into values of one other type P or P'Class: type P is abstract tagged ...; -- All possible parameters type T is abstract tagged ...; -- The type procedure X (Object : in out T; Parameters : P'Class) is abstract; A client have to override X. It can also derive from P its own set of parameters. BTW, this is the typical case where multiple dispatch would be appropriate and more safe: procedure X (Object : in out T; Parameters : P) is abstract; Like in: Print (Output : in out Device; Object : Thing) is abstract; Without multiple dispatch each client should take care of selecting an appropriate way of dealing with all possible parameters that may occur in P'Class. Here we come to another problem. You can say, OK, but each client has its own unique set of parameters. Why should I care about the cross-variants coming from other clients? This is the problem of parallel type hierarchies. When you derive from T you want either to automatically derive or to force a deriving from P. These pairs of derived types have to be bound. In terms of multiple dispatch it is the case when the joint dispatch table has to be diagonal. The most famous examples are Object-Container_Of, Object-Handle types pairs. When you derive a new object, you also want to have a specialized handle type (or access type) to it. AFAIK, this problem is not solved in either Ada or any other OO language I know. > I'm interested *specifically* in how this > works with an abstract tagged record and the inheritance heierarchy that > goes with it, so please don't suggest that I'm going about it all wrong > and I really need to do something with a generic, etc... If my > understanding is correct and it can't be done then we just trash that > notion and go to other techniques, but I want to know how this > capability works. Thanks. And so we return to the very first question, why there should be a class? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de