comp.lang.ada
 help / color / mirror / Atom feed
* When to use 'Class in a parameter list
@ 2001-07-23 20:27 Marin David Condic
  2001-07-23 21:39 ` Ehud Lamm
                   ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Marin David Condic @ 2001-07-23 20:27 UTC (permalink / raw)


O.K. Here's something I thought I understood but given behavior of some code
I have, now I'm questioning what is happening.

If I have a tagged type "Base_Type" that has an operation on it, called "Op"
and I derive a new type called "Child_Type" that does not require changes to
"Op", do I make the parameter Base_Type or Base_Type'Class? (My
understanding was that I could make it Base_Type, but then calls to it with
a Child_Type would require explicit type conversion. Making it 'Class would
accept anything of that class without conversion. Apparently the compiler is
swollowing it without type conversion - which is now confusing me.)

In code:

procedure Op (Base : in out Base_Type) ;
...
X : Child_Type ;
...
Op (X) ; --  Why is this working without a Base_Type (X) conversion???

So unless I'm doing something strange that is causing some corner-case to
come up, I'm now wondering why I would need Base_Type'Class as a parameter
type? I was under the impression that I would use 'Class if I wanted to make
an operation that worked on anything derived from the class without explicit
conversion. (Possible to override it in a child class, AFAIK...)

My understanding of when to *NOT* use the 'Class was if I was building an
operation I expected to override (possibly calling the parent operation
within it - using a type conversion). So when it is not overriden, and
control goes to the parent op without an explicit conversion, then when do
you need the 'Class? I must be missing something here......(I need to do
this sort of thing more often - it all evaporates if you don't use it!!!)

MDC
--
Marin David Condic
Senior Software Engineer
Pace Micro Technology Americas    www.pacemicro.com
Enabling the digital revolution
e-Mail:    marin.condic@pacemicro.com
Web:      http://www.mcondic.com/






^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic
@ 2001-07-23 21:39 ` Ehud Lamm
  2001-07-24 12:49   ` Marin David Condic
  2001-07-23 22:55 ` Stephen Leake
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 14+ messages in thread
From: Ehud Lamm @ 2001-07-23 21:39 UTC (permalink / raw)


Marin David Condic <marin.condic.auntie.spam@pacemicro.com> wrote in message
news:9ji1b3$4pi$1@nh.pace.co.uk...
> So unless I'm doing something strange that is causing some corner-case to
> come up, I'm now wondering why I would need Base_Type'Class as a parameter
> type? I was under the impression that I would use 'Class if I wanted to
make
> an operation that worked on anything derived from the class without
explicit
> conversion. (Possible to override it in a child class, AFAIK...)


You mean _impossible_ right?

Basically if you want overriding to be an option you use a primitive
operation. Notice that you have to think about the possible evolution of the
system, not about how things work the first time around.

The main question is not about the conversion, but whether calling the
routine requires run-time dispatching. (essentialy, the class-wide routine
doesn't need dispatching). The Rationale has a couple of tables that nicely
show the dispatching/non-dispatching semantics of the various parameter
lists.

It may be helpful to think of class-wide routines as a language incarnation
of the "template method" design pattern. They allow you to specify a general
alogirthm that may work differently on different derived types, by invoking
dispatching routines.
(Now, I must admit, this would work better with MI, but MI is evil...)

Ehud Lamm






^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic
  2001-07-23 21:39 ` Ehud Lamm
@ 2001-07-23 22:55 ` Stephen Leake
  2001-07-25 19:20   ` Deligation with Ada95 Hans-Olof Danielsson
  2001-07-24  2:22 ` When to use 'Class in a parameter list Vincent Marciante
  2001-10-29 22:52 ` Matthew Heaney
  3 siblings, 1 reply; 14+ messages in thread
From: Stephen Leake @ 2001-07-23 22:55 UTC (permalink / raw)


"Marin David Condic" <marin.condic.auntie.spam@pacemicro.com> writes:

> O.K. Here's something I thought I understood but given behavior of some code
> I have, now I'm questioning what is happening.
> 
> <snip>
> 
> In code:
> 
> procedure Op (Base : in out Base_Type) ;
> ...

just for clarity:

type Child_Type is new Base_Type; 

> X : Child_Type ;
> ...
> Op (X) ; --  Why is this working without a Base_Type (X)
> conversion???

Because when you declare a derived type (Child_Type), you implicitly
declare all the 'primitive operations' of the parent type, for the
child type. So you now have:

procedure Op (Base : in out Child_Type);

This is the whole idea of derived types; it is important to understand
this feature.
 
> So unless I'm doing something strange that is causing some corner-case to
> come up, I'm now wondering why I would need Base_Type'Class as a parameter
> type? I was under the impression that I would use 'Class if I wanted to make
> an operation that worked on anything derived from the class without explicit
> conversion. 

Yes, this is true.

> (Possible to override it in a child class, AFAIK...)

I'm not clear what you mean by this. If you declare:

procedure Class_Wide_Op (Foo : in Base_Type'class);

then it is _not_ a 'primitive operation', since the type of the
argument is _not_ 'Base_Type' (Base_type'class is a _different_
type!). Only 'primitive operations' can be overridden in derived
types.

> My understanding of when to *NOT* use the 'Class was if I was building an
> operation I expected to override (possibly calling the parent operation
> within it - using a type conversion). So when it is not overriden, and
> control goes to the parent op without an explicit conversion, then when do
> you need the 'Class? I must be missing something here......(I need to do
> this sort of thing more often - it all evaporates if you don't use
> it!!!)

Hmm. When you do not explicitly override Op, the implicitly declared
operation gets an implicit body that is the same as the parent body.

Another difference between a 'Class parameter and a plain parameter;
when you call a primitive operation with a 'Class value, you get
run-time dynamic dispatching. When you call a primitive operation with
a plain value, you get compile-time static dispatching. I'm not clear
if this matters in your case.

-- 
-- Stephe



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic
  2001-07-23 21:39 ` Ehud Lamm
  2001-07-23 22:55 ` Stephen Leake
@ 2001-07-24  2:22 ` Vincent Marciante
  2001-07-24 12:52   ` Marin David Condic
  2001-10-29 22:52 ` Matthew Heaney
  3 siblings, 1 reply; 14+ messages in thread
From: Vincent Marciante @ 2001-07-24  2:22 UTC (permalink / raw)


Marin David Condic wrote:
> 
> In code:
> 
> procedure Op (Base : in out Base_Type) ;
> ...
> X : Child_Type ;
> ...
> Op (X) ; --  Why is this working without a Base_Type (X) conversion???
> 

Assuming that Op is defined as a primative of Base_Type then
a procedure

   procedure Op (Base : in out Child_Type) ;

automaticaly exists as a consequence of having derived
Child_Type from Base_Type.  

This is the case for all derived types and has nothing 
to do with the taggedness of the types in question.



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-23 21:39 ` Ehud Lamm
@ 2001-07-24 12:49   ` Marin David Condic
  2001-07-24 14:39     ` Dmitry A. Kazakov
  2001-07-24 15:16     ` Ehud Lamm
  0 siblings, 2 replies; 14+ messages in thread
From: Marin David Condic @ 2001-07-24 12:49 UTC (permalink / raw)


"Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message
news:9ji5j7$hlu$1@news.huji.ac.il...
> > type? I was under the impression that I would use 'Class if I wanted to
> make
> > an operation that worked on anything derived from the class without
> explicit
> > conversion. (Possible to override it in a child class, AFAIK...)
>
>
> You mean _impossible_ right?
>
Uhhhh Dunno... I seem to have a procedure at one level that takes an
Object'Class parameter and I overrode it one level up with an identical
procedure taking an Object_Child'Class. The compiler didn't complain and the
code appears to work. Maybe it shouldn't, but there you have it...


> Basically if you want overriding to be an option you use a primitive
> operation. Notice that you have to think about the possible evolution of
the
> system, not about how things work the first time around.
>
Primitive operation? What do you mean? Like if I have a "procedure C (Obj :
in out Object);" as opposed to "procedure C (Obj : in out Object'Class);"?


> The main question is not about the conversion, but whether calling the
> routine requires run-time dispatching. (essentialy, the class-wide routine
> doesn't need dispatching). The Rationale has a couple of tables that
nicely
> show the dispatching/non-dispatching semantics of the various parameter
> lists.
>
O.K. So if I make it a 'Class, it does not require runtime dispatching? The
compiler knows from the type that it can simply degenerate to the specific
operation, rather than jump to a dispatch table? And if I override the
'Class operation (it *seems* to work!) with a new 'Class operation, the
compiler can tell from the data type what to jump to?


> It may be helpful to think of class-wide routines as a language
incarnation
> of the "template method" design pattern. They allow you to specify a
general
> alogirthm that may work differently on different derived types, by
invoking
> dispatching routines.
> (Now, I must admit, this would work better with MI, but MI is evil...)
>
Now you've got me thinking it should dispatch. Does it or does it not? I
sort of conceptualized the 'Class operations (based on examples from a
couple of books - which I badly need to re-read apparently! :-) as "This
procedure should work on anything derived from this class because it only
operates on the base-level components and generally should not be something
that is overriden". Whereas, the operations on some base type without the
'Class operation seemed to be saying to me "O.K. This operation works on the
base type, but you will probably be overriding it to provide additional
capabilities when you derive a child type." Perhaps my conception of this is
inaccurate...

Maybe you can help me get my brain straightened out about this. I vaguely
remember understanding this a couple of years ago when I was last fooling
with it, but not having used it recently or enough, it is confusing me
again.

MDC
--
Marin David Condic
Senior Software Engineer
Pace Micro Technology Americas    www.pacemicro.com
Enabling the digital revolution
e-Mail:    marin.condic@pacemicro.com
Web:      http://www.mcondic.com/






^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24  2:22 ` When to use 'Class in a parameter list Vincent Marciante
@ 2001-07-24 12:52   ` Marin David Condic
  2001-07-24 14:36     ` Ed Falis
  0 siblings, 1 reply; 14+ messages in thread
From: Marin David Condic @ 2001-07-24 12:52 UTC (permalink / raw)


O.K. I think I've got my brain straight about that - maybe the type
conversion that I thought I needed only needed to occur within the child
package in order to force a call back to its parent routine. (Resolving an
otherwise ambiguous call.)

But that still leaves me a bit confused about when I want to make a
parameter of 'Class.

MDC
--
Marin David Condic
Senior Software Engineer
Pace Micro Technology Americas    www.pacemicro.com
Enabling the digital revolution
e-Mail:    marin.condic@pacemicro.com
Web:      http://www.mcondic.com/


"Vincent Marciante" <marciant_antispam@li.net> wrote in message
news:3B5CDBCA.5810@li.net...
>
> Assuming that Op is defined as a primative of Base_Type then
> a procedure
>
>    procedure Op (Base : in out Child_Type) ;
>
> automaticaly exists as a consequence of having derived
> Child_Type from Base_Type.
>
> This is the case for all derived types and has nothing
> to do with the taggedness of the types in question.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24 12:52   ` Marin David Condic
@ 2001-07-24 14:36     ` Ed Falis
  2001-07-24 15:29       ` Ehud Lamm
  0 siblings, 1 reply; 14+ messages in thread
From: Ed Falis @ 2001-07-24 14:36 UTC (permalink / raw)


Marin David Condic wrote:
> 
> O.K. I think I've got my brain straight about that - maybe the type
> conversion that I thought I needed only needed to occur within the child
> package in order to force a call back to its parent routine. (Resolving an
> otherwise ambiguous call.)
> 
> But that still leaves me a bit confused about when I want to make a
> parameter of 'Class.

There are two situations I find useful for using classwide parameters.

The first is when I want to force that the implementation of a routine
will not be overridden by derived type.

The second is for implementing a "template pattern", where some overall
processing is applied regardless of the specific type of the parameter,
and dispatching is used within the routine to handle variation among
specific types.  For instance, when traversing a tree, certain steps are
applied to all nodes, whether internal or leaf, while others depend on
the status of the node (composite pattern).  So here the traversal
operation would use a classwide parameter, while the action applied
within it, would dispatch.

- Ed



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24 12:49   ` Marin David Condic
@ 2001-07-24 14:39     ` Dmitry A. Kazakov
  2001-07-24 15:16     ` Ehud Lamm
  1 sibling, 0 replies; 14+ messages in thread
From: Dmitry A. Kazakov @ 2001-07-24 14:39 UTC (permalink / raw)


On Tue, 24 Jul 2001 08:49:36 -0400, "Marin David Condic"
<marin.condic.auntie.spam@pacemicro.com> wrote:

>O.K. So if I make it a 'Class, it does not require runtime dispatching? The
>compiler knows from the type that it can simply degenerate to the specific
>operation, rather than jump to a dispatch table?

No there is no specific operation. A class-wide operation works for
all derived types.

>Now you've got me thinking it should dispatch. Does it or does it not?

Never. A class-wide actual is passed as-is.

>I sort of conceptualized the 'Class operations (based on examples from a
>couple of books - which I badly need to re-read apparently! :-) as "This
>procedure should work on anything derived from this class because it only
>operates on the base-level components and generally should not be something
>that is overriden". Whereas, the operations on some base type without the
>'Class operation seemed to be saying to me "O.K. This operation works on the
>base type, but you will probably be overriding it to provide additional
>capabilities when you derive a child type." Perhaps my conception of this is
>inaccurate...
>
>Maybe you can help me get my brain straightened out about this. I vaguely
>remember understanding this a couple of years ago when I was last fooling
>with it, but not having used it recently or enough, it is confusing me
>again.

I consider class-wide operations as a substitution for generic
routines parametrized by a type. The possible values of the generic
parameter are restricted to the class (the set of derived types). The
goal is same as for generics: to write code that works for some set of
types. Advantages are: no instantiation required, a class-wide
operation can be put into a library etc.

Regards,
Dmitry Kazakov



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24 12:49   ` Marin David Condic
  2001-07-24 14:39     ` Dmitry A. Kazakov
@ 2001-07-24 15:16     ` Ehud Lamm
  2001-07-24 17:16       ` Marin David Condic
  1 sibling, 1 reply; 14+ messages in thread
From: Ehud Lamm @ 2001-07-24 15:16 UTC (permalink / raw)



Marin David Condic <marin.condic.auntie.spam@pacemicro.com> wrote in message
news:9jjqt0$o1s$1@nh.pace.co.uk...
> "Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message
> news:9ji5j7$hlu$1@news.huji.ac.il...
> > > type? I was under the impression that I would use 'Class if I wanted
to
> > make
> > > an operation that worked on anything derived from the class without
> > explicit
> > > conversion. (Possible to override it in a child class, AFAIK...)
> >
> >
> > You mean _impossible_ right?
> >
> Uhhhh Dunno... I seem to have a procedure at one level that takes an
> Object'Class parameter and I overrode it one level up with an identical
> procedure taking an Object_Child'Class. The compiler didn't complain and
the
> code appears to work. Maybe it shouldn't, but there you have it...
>

I didn't mean to imply that you can't write this kind of routine. Only that
the intuition is the reverse. But this is *not* overriding exactly, since
the inheritance relation is between Object and Object_Child and not between
the class-wide types. Again, the real isssue is dispatching, as explained in
the retional.
You may want to keep in mind that we are not just thinking about formal
parameters. The language has class-wide types, so you can have class-wide
value (usually an access Obj'class kind of thing). The important relations
are between these and dispatching, as shown in this table from the
Rationale:



     +----------+---------------------------------+
     |              |                  formal               |
     |   actual |        specific   |    class-wide |
     +----------+----------------+----------------+
     |              |                       |                    |
     | specific |  static binding|  class-wide op |
     |              |                      |                    |
     |----------+--------------   --+----------------|
     |             |                          |                  |
     |class-wide|   dispatching  | class-wide op  |
     |              |                         |                |
     +----------+---------------------------------+

                  Table 4-1: Kinds of Binding


An operand used to control dispatching is called a controlling operand. A
primitive operation may have several controlling operands; a primitive
function may also have a controlling result.

(Sorry if the mail software destroys the layout...)

>
> > Basically if you want overriding to be an option you use a primitive
> > operation. Notice that you have to think about the possible evolution of
> the
> > system, not about how things work the first time around.
> >
> Primitive operation? What do you mean? Like if I have a "procedure C (Obj
:
> in out Object);" as opposed to "procedure C (Obj : in out Object'Class);"?

Yes. Again let me quote the paragraph from the Rationale:

"With the increased importance of derived types for object oriented
programming in Ada 95, the notion of the operations closely related to a
type in this manner is generalized. The primitive operations of a type are
those that are implicitly provided for the type and, for types immediately
declared in a package specification, all subprograms with an operand or
result of the type declared anywhere in that package specification. The
domain is therefore extended to include the private part (but not the body).

Thus, in Ada 95, the derivable operations of Ada 83 have become "primitive
operations" and the restriction of these operations to the visible part of a
package has been eliminated. These changes support added capability:
primitive operations may be private and a type and its derivatives may be
declared in the same declarative region (this property is useful for
building related abstractions and was used in the package New_Alert_System
of Part One).

Primitive operations clarify the notion of an abstract data type for
purposes of object oriented programming (inheritance and polymorphism) and
genericity. They are distinguished from the other operations of a type in
the following ways

* Inheritance. Primitive operations are the derivable (inherited)
operations.
* Polymorphism. Primitive operations are dispatching operations on tagged
types.
* Genericity. Primitive operations are the ones available within generic
templates parameterized by a class. "

The key line here is the one about polymorphism.

>
>
> > The main question is not about the conversion, but whether calling the
> > routine requires run-time dispatching. (essentialy, the class-wide
routine
> > doesn't need dispatching). The Rationale has a couple of tables that
> nicely
> > show the dispatching/non-dispatching semantics of the various parameter
> > lists.

> Now you've got me thinking it should dispatch. Does it or does it not? I
> sort of conceptualized the 'Class operations (based on examples from a
> couple of books - which I badly need to re-read apparently! :-) as "This
> procedure should work on anything derived from this class because it only
> operates on the base-level components and generally should not be
something
> that is overriden". Whereas, the operations on some base type without the
> 'Class operation seemed to be saying to me "O.K. This operation works on
the
> base type, but you will probably be overriding it to provide additional
> capabilities when you derive a child type." Perhaps my conception of this
is
> inaccurate...
>

The way I explain it to my students is like this "from the 'outside' of the
class-wide routine there no dispatching" (i.e., no dispatching on call to
the class-wide routine), "from the 'inside' you can have dispathcing, by
calling the dispatching operations" (i.e, calls to primitive routine, made
inside the class-wide routine).
Notice that when you call a  routine that expect a specific type, from
inside a class-wide routine you are, in a sense, giving a class-wide
actual - and per the table above you may have dispatching.

Another way at looking at this, is to realize that the class-wide operation
behave the same for all type in the hierarchy, up to calls to dispatching
routines. The commonality is expressed by creating a class-wide routine; the
routine can be sensitive to the differences between the types in the
hierarchy by calling type specific (dispatching) routines.
Aside from the concept of "template function pattern", another common term
is "factoring out common behaviours" - this may give a better idea what's
this all about from a design POV.

Ehud Lamm





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24 14:36     ` Ed Falis
@ 2001-07-24 15:29       ` Ehud Lamm
  0 siblings, 0 replies; 14+ messages in thread
From: Ehud Lamm @ 2001-07-24 15:29 UTC (permalink / raw)



Ed Falis <efalis@mediaone.net> wrote in message
news:3B5D8819.E9D8AD4E@mediaone.net...
> There are two situations I find useful for using classwide parameters.
>
> The first is when I want to force that the implementation of a routine
> will not be overridden by derived type.

Indedd. I failed to mention this reason, since it seemed like this is what
Marin focused on, where as I find the second reason of greater design
importance. But it is useful to keep this in mind (as well as trick for
making routines non-primitive, like declaring them in a child unit).

>
> The second is for implementing a "template pattern", where some overall
> processing is applied regardless of the specific type of the parameter,
> and dispatching is used within the routine to handle variation among
> specific types.  For instance, when traversing a tree, certain steps are
> applied to all nodes, whether internal or leaf, while others depend on
> the status of the node (composite pattern).  So here the traversal
> operation would use a classwide parameter, while the action applied
> within it, would dispatch.
>

Another example is having a "traversable" interface with
get_first/get_next/end_of_sequence operations, and creating various general
algorithms (like filters).

Ehud Lamm





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-24 15:16     ` Ehud Lamm
@ 2001-07-24 17:16       ` Marin David Condic
  0 siblings, 0 replies; 14+ messages in thread
From: Marin David Condic @ 2001-07-24 17:16 UTC (permalink / raw)


...And then it hit me like the wet kiss at the end of a fist....
(Lightbulb!) Ahhhhhh! Its all coming back to me as if out of an alcoholic
stupor...

O.K. This has me pretty much back on track. I want the class-wide flavor if
inside the routine I want to generalize some part of the operation and
possibly dispatch to other operations that may depend on the specific type
of object sent in.

I really need to work with this stuff more than once a year... :-)

MDC
--
Marin David Condic
Senior Software Engineer
Pace Micro Technology Americas    www.pacemicro.com
Enabling the digital revolution
e-Mail:    marin.condic@pacemicro.com
Web:      http://www.mcondic.com/


"Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message
news:9jk3lr$ki0$1@news.huji.ac.il...
>
>
> The way I explain it to my students is like this "from the 'outside' of
the
> class-wide routine there no dispatching" (i.e., no dispatching on call to
> the class-wide routine), "from the 'inside' you can have dispathcing, by
> calling the dispatching operations" (i.e, calls to primitive routine, made
> inside the class-wide routine).
> Notice that when you call a  routine that expect a specific type, from
> inside a class-wide routine you are, in a sense, giving a class-wide
> actual - and per the table above you may have dispatching.
>
> Another way at looking at this, is to realize that the class-wide
operation
> behave the same for all type in the hierarchy, up to calls to dispatching
> routines. The commonality is expressed by creating a class-wide routine;
the
> routine can be sensitive to the differences between the types in the
> hierarchy by calling type specific (dispatching) routines.
> Aside from the concept of "template function pattern", another common term
> is "factoring out common behaviours" - this may give a better idea what's
> this all about from a design POV.
>
> Ehud Lamm
>
>





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Deligation with Ada95
  2001-07-23 22:55 ` Stephen Leake
@ 2001-07-25 19:20   ` Hans-Olof Danielsson
  2001-07-26  2:06     ` Lao Xiao Hai
  0 siblings, 1 reply; 14+ messages in thread
From: Hans-Olof Danielsson @ 2001-07-25 19:20 UTC (permalink / raw)
  To: comp.lang.ada

Hi,

Some experts state that _deligation_ is a technique used in OOP
to avoid subclassing reducing the complexity of a system. If some
of you know recourses, other than ACM Ada Letters, Nov/Dec
1994, covering this technique applied with Ada 95,
would you please apoint me those other recources.

Tia

HOD





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Deligation with Ada95
  2001-07-25 19:20   ` Deligation with Ada95 Hans-Olof Danielsson
@ 2001-07-26  2:06     ` Lao Xiao Hai
  0 siblings, 0 replies; 14+ messages in thread
From: Lao Xiao Hai @ 2001-07-26  2:06 UTC (permalink / raw)




Hans-Olof Danielsson wrote:

> Hi,
>
> Some experts state that _deligation_ is a technique used in OOP
> to avoid subclassing reducing the complexity of a system. If some
> of you know recourses, other than ACM Ada Letters, Nov/Dec
> 1994, covering this technique applied with Ada 95,
> would you please apoint me those other recources.

The best example of delegation in Ada 95 is the requeue.   In
delegation,
one object asks another to do the actual work requested of it.   That
class
can then forget about the delegation.   The delegated class can respond
directly to the original requestor.   This is not like a procedure call
where
the original request must unwind through a stack.   It is not like an
entry
call where the request is kept on the queue of the accept statement.
The
actual requeue does not block the delegator the way an entry call would.

There are actually very few languages that implement this idea directly.

C++, in its first design, was intended to include this feature, but it
was
eliminated for some reason.

Richard Riehle
AdaWorks Software Engineering
richard@adaworks.com




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: When to use 'Class in a parameter list
  2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic
                   ` (2 preceding siblings ...)
  2001-07-24  2:22 ` When to use 'Class in a parameter list Vincent Marciante
@ 2001-10-29 22:52 ` Matthew Heaney
  3 siblings, 0 replies; 14+ messages in thread
From: Matthew Heaney @ 2001-10-29 22:52 UTC (permalink / raw)



"Marin David Condic" <marin.condic.auntie.spam@pacemicro.com> wrote in
message news:9ji1b3$4pi$1@nh.pace.co.uk...
> O.K. Here's something I thought I understood but given behavior of some
code
> I have, now I'm questioning what is happening.
>
> If I have a tagged type "Base_Type" that has an operation on it, called
"Op"
> and I derive a new type called "Child_Type" that does not require changes
to
> "Op", do I make the parameter Base_Type or Base_Type'Class?

Let's go back to Ada83, which already had inheritance (of "primitive"
operations):

package P is
   type T is limited private;
   procedure Op (O : in out T);
...
end P;

Now let's create a type that derives from T:

with P;
package Q is
   type NT is new P.T;
end Q;

The type NT inherits the operation Op from its parent type T, which means I
can do this:

declare
   O : Q.NT;
begin
   Q.Op (O);
end;

The point is that at the point of declaration of NT, operation Op is
"implicitly declared."  This was true in Ada83, and it continues to be true
in Ada95.  (This is quite deliberate: Tucker's philosophy was that Ada95
build on the existing infrastructure already present in Ada83.)

Now let's review what we mean by "primitive" operation.  Basically, it's an
operation that takes the type as a parameter or return value, and is
declared in the same package as the type.  The operation P.Op is primitive
for type P.T, because it takes type T as a parameter.

Not all operations that take the type as a parameter are primitive.  For
example:

with P;
package PP is
   procedure Another_Op (O : in out P.T);
end;

Here, operation Another_Op is NOT primitive.

We care about "primitive" operations for a type, because they are inherited
during a derivation.  That's why type Q.NT automatically has an operation
Q.Op -- because it inherited it from its parent P.T.  For whatever reason,
this is an aspect of Ada that few programmers seem to fully understand.

Of course, type NT is free to override Op, if it doesn't like the default
implementation:

with P;
package Q is
   type NT is new P.T;
   procedure Op (O : in out NT);
end Q;

Everything I've just said applies to Ada95.  The only difference between
Ada83 and Ada95 is that Ada95 added "type extension" to the language.  The
inheritance model *already* existed in Ada83.  The code above is an Ada95
program, but for the example let's rewrite it to use tagged types:

package P is
   type T is tagged limited private;
   procedure Op (O : in out T);
...
end P;

with P;
package Q is
   type NT is new P.T with null record;
end;

(Note that in general, you should create a package hierarchy that mimics the
type hierarchy.  Here we're trying to keep the example simple.)

Everything is the same as in our earlier example.  Type Q.NT inherits the
operation Op from its parent type P.T, so you can do this (same as before):

declare
   O : Q.NT;
begin
   Q.Op (O);
end;

Now, we talked about "primitive" operations.  Operation Op is primitive for
type T (and NT), because it takes type T as a parameter, and is declared in
the same package as T (here, package P).

You asked whether you should declare the parameter as type T'Class: the
answer is NO.  The reason is that were T'Class the type, then the operation
doesn't satisfy the criteria for "primitiveness".  The pararmeter type would
be T'Class, not T, and the operation has to take type T in order for it to
qualify as "primitive".

Operations that are NOT primitive for the type are NOT inherited during a
derivation.  So if you were to do this:

package P is
   type T is tagged limited private;
   procedure Op (O : in out T'Class);
...
end P;

with P;
package Q is
   type NT is new P.T with null record;
end;

declare
   O : Q.NT;
begin
   Q.Op (O);  --will not compile
end;

This will NOT compile, because type Q.NT does NOT have an operation called
Op.  Op is not primitive, and therefore it is not inherited.

You want to know when you should declare the operation as taking type
T'Class, but your question should really be phrased as "when should the
operation be primitive?", or "when should an operation be class-wide?".

Does the operation apply to every type in the class, or does it make sense
for derived types to provide their own type-specific implementation?

One characteristic of class-wide operations is that they have a fixed
algorithm, but operations called to implement the algorithm can vary across
types.  (As Ehud pointed out, this is a design pattern called "Template
Method.")  Class-wide operations are ultimately implemented by calling
primitive operations of the type, which dispatch according to the tag of the
object.


> (My
> understanding was that I could make it Base_Type, but then calls to it
with
> a Child_Type would require explicit type conversion.

You are confused.  You don't need to convert Child_Type to Base_Type in
order to call Op, because Child_Type already has an operation called Op.
The only reason you'd need to do a conversion (here, a "view" conversion) is
to call the parent's implementation of Op.  For example:

package P is
   type Base_Type is tagged null record;
   procedure Op (O : in out T);
end;

package P.C is
   type Child_Type is new Base_Type with null record;
end;

declare
   O : P.C.Child_Type;
begin
   P.C.Op (O);  --OK
end;

This is a perfectly reasonable thing to do.  No conversion is required.

Now let's say Child_Type overrides Op:

package P.C is
   type Child_Type is new Base_Type with null record;
   procedure Op (O : in out Child_Type);
end;

Now let's implement P.C.Op, by calling the parent version of Op:

package body P.C is
   procedure Op (O : in out Child_Type) is
   begin
      Op (Base_Type(O));
      --do some more stuff
   end;
end P.C;

Here we've performed a "view" conversion, in order to call the Op defined
for Base_Type.  You often do this when implementing a type that derives from
Controlled:

package P is
   type T is new Limited_Controlled with null record;
   procedure Finalize (O : in out T);
end;

package P.C is
   type NT is new T with null record;
   procedure Finalize (O : in out NT);
end;

package body P.C is
   procedure Finalize (O : in out NT) is
   begin
      --do type-specific clean-up
      Finalize (T(O)):
   end;
...
end P.C;

This technique ensures that "base class" finalization is done too.


> Making it 'Class would
> accept anything of that class without conversion. Apparently the compiler
is
> swollowing it without type conversion - which is now confusing me.)

No type conversion is necessary.  If the operation is primitive for
Base_Type, it is inherited by Child_Type during derivation, so therefore
Child_Type has the operation.


> In code:
>
> procedure Op (Base : in out Base_Type) ;
> ...
> X : Child_Type ;
> ...
> Op (X) ; --  Why is this working without a Base_Type (X) conversion???

Because you're simply calling the Op defined for Child_Type.  Op was
implicitly declared at the point of declaration of Child_Type, because Op is
primtive, and therefore is inherited during a derivation.


> So unless I'm doing something strange that is causing some corner-case to
> come up, I'm now wondering why I would need Base_Type'Class as a parameter
> type?

Making the operation take T'Class changes the semantics of the operation.
It means "this operation applies to all types in the class."  Same as a
static method in C++.

> I was under the impression that I would use 'Class if I wanted to make
> an operation that worked on anything derived from the class without
explicit
> conversion. (Possible to override it in a child class, AFAIK...)

You appear to be confused about when operations implicitly declared for a
type.  Op is implicitly declared for Child_Type, because it was primitive
for Base_Type, and was therefore inherited.

An operation that takes Base_Type'Class works "without conversion" because
type Base_Type'Class "covers" type Child_Type.  In a sense the types
Base_Type'Class and Child_Type have a subtype relationship, the way Positive
is related to type Integer.


> My understanding of when to *NOT* use the 'Class was if I was building an
> operation I expected to override (possibly calling the parent operation
> within it - using a type conversion).

Yes.  Like the Finalize example above.

> So when it is not overriden, and
> control goes to the parent op without an explicit conversion, then when do
> you need the 'Class?

It was not overridden, but it is still defined for type Child_Type.

> I must be missing something here......(I need to do
> this sort of thing more often - it all evaporates if you don't use it!!!)

Your confusion probably stems from an incomplete understanding of Ada83
semantics.

Regards,
Matt







^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2001-10-29 22:52 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic
2001-07-23 21:39 ` Ehud Lamm
2001-07-24 12:49   ` Marin David Condic
2001-07-24 14:39     ` Dmitry A. Kazakov
2001-07-24 15:16     ` Ehud Lamm
2001-07-24 17:16       ` Marin David Condic
2001-07-23 22:55 ` Stephen Leake
2001-07-25 19:20   ` Deligation with Ada95 Hans-Olof Danielsson
2001-07-26  2:06     ` Lao Xiao Hai
2001-07-24  2:22 ` When to use 'Class in a parameter list Vincent Marciante
2001-07-24 12:52   ` Marin David Condic
2001-07-24 14:36     ` Ed Falis
2001-07-24 15:29       ` Ehud Lamm
2001-10-29 22:52 ` Matthew Heaney

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