comp.lang.ada
 help / color / mirror / Atom feed
From: "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de>
Subject: Re: Abstract Operations On A Tagged Record
Date: Sat, 30 Oct 2004 16:34:34 +0200
Date: 2004-10-30T16:34:34+02:00	[thread overview]
Message-ID: <972rdspp3zvm.brfeteze2k67.dlg@40tude.net> (raw)
In-Reply-To: FjMgd.12476$5i5.2958@newsread2.news.atl.earthlink.net

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 (<TBD Parameter List>) 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



  reply	other threads:[~2004-10-30 14:34 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-10-30 13:04 Abstract Operations On A Tagged Record Marin David Condic
2004-10-30 14:34 ` Dmitry A. Kazakov [this message]
2004-10-30 16:07 ` Nick Roberts
2004-11-01 12:58   ` Marin David Condic
2004-11-01 18:09     ` Nick Roberts
2004-11-01 22:27       ` Marin David Condic
2004-11-06 13:17       ` Marin David Condic
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox