comp.lang.ada
 help / color / mirror / Atom feed
* Q: Primitive operation of a type
@ 1997-06-25  0:00 Van Snyder
  1997-07-01  0:00 ` Matthew Heaney
  1997-07-02  0:00 ` Martin C. Carlisle
  0 siblings, 2 replies; 7+ messages in thread
From: Van Snyder @ 1997-06-25  0:00 UTC (permalink / raw)



I've looked at Barnes's book, and Cohen's book, and I can't
decide whether it's possible for a procedure to be a primitive
operation of more than one type.

All they say is that a primitive operation of a type is a
procedure declared in the same package specification as the
type, and having an argument or result of the type.  The
question of other arguments is not discussed.

If it's not possible, please send me a reference to the
relevant parts of the LRM (or Barnes's book or Cohen's book),
so I can learn the precise rules that apply (and you might as
well ignore the remainder of this posting).

If it is possible, please explain how over-riding works in this
case:

Suppose T1 and T2 are tagged types, and T11 and T21 are
extensions thereof.  Suppose P12 is a procedure that has
arguments of types T1 and T2, P112 has arguments of types T11
and T2, and P121 has arguments of types T1 and T21, and suppose
that P12, P112 and P121 all have the same name, P -- so P112 and
P121 over-ride P12, but for different arguments and possibly for
different derivation classes.

Which procedure is used when P is invoked with arguments of
types T11 and T21?  Please refer me to sections of the LRM (or
Barnes's or Cohen's books) that explain the answer.

Thanks in advance for your help.

-- 
What fraction of Americans believe   |  Van Snyder
Wrestling is real and NASA is fake?  |  vsnyder@math.jpl.nasa.gov




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

* Re: Q: Primitive operation of a type
  1997-06-25  0:00 Q: Primitive operation of a type Van Snyder
@ 1997-07-01  0:00 ` Matthew Heaney
  1997-07-02  0:00   ` Mats Weber
  1997-07-02  0:00 ` Martin C. Carlisle
  1 sibling, 1 reply; 7+ messages in thread
From: Matthew Heaney @ 1997-07-01  0:00 UTC (permalink / raw)



In article <5oq51h$t7u@netline.jpl.nasa.gov>, vsnyder@math.jpl.nasa.gov
(Van Snyder) wrote:

>I've looked at Barnes's book, and Cohen's book, and I can't
>decide whether it's possible for a procedure to be a primitive
>operation of more than one type.

It is, but an operation can only be primitive for one tagged type.  

For example, the operation Set_Col in package Ada.Text_IO is primitive for
both type File_Type and type Positive_Count.  But note that neither of
these types are tagged.

These issues are discussed in Mats Weber's PhD thesis.

>If it's not possible, please send me a reference to the
>relevant parts of the LRM. [snip]

No, it is not possible for an operation to be primitive for more than one
tagged type.

>Which procedure is used when P is invoked with arguments of
>types T11 and T21?  Please refer me to sections of the LRM (or
>Barnes's or Cohen's books) that explain the answer.

Be careful though, to not interpret "can't be primitive for more than one
type" to mean "can't have different tagged types as subprogram parameters." 
A operation can be primitive for one tagged type, but take additional
class-wide parameters of another type.  For example,

package P is

   type T1 is tagged private;

   type T2 is tagged private;

   procedure Op1 (O1 : T1; O2 : T2);  -- illegal

   procedure Op2 (O1 : T1; O2 : T2'Class);  -- AOK

   procedure Op3 (O1 : T1'Class; O2 : T2);  -- AOK2

   procedure Op4 (O : T1'Class);

   procedure Op5 (O : T2'Class);
 
   procedure Op6 (O1 : T1'Class; O2 : T2'Class);

   procedure Op7 (O1 : T1; O2 : T1);  -- OK
...
end P;

Op1 is illegal, because it takes more than one kind of tagged type as
parameters.

Op2 is a primitive operation of type T1, and it gets inherited by types
that derive from T1.

Op3 is a primitive opeation of type T2, and it gets inherited by types that
derive from T2.

Op2, because it takes an object of type T2'Class, is not a primitive
operation of type T2.

Op3, because it takes an object of type T1'Class, is not a primitive
operation of type T3.

Op4 is not a primitive operation of any type.  Ditto for Op5 and Op6.

Op7 is a primitive operation of type T1, and gets inherited by types that
derive from T1.  It's OK to have more than one parameter of a tagged type,
it's just that they have to have the same tag.

Op7 is interesting, because sometimes a run-time check will need to be made
to ensure that both objects really have the same tag.  For example,

procedure P (O1, O2 : T1'Class) is
begin
   Op7 (O1, O2);
end;

This will dispatch on the tag of the O1 and O2, but only if O1 and O2 have
the same tag, which can't be determined until run-time.  If the tag-check
fails, then an exception is raised (I forget which, either Constraint_Error
or Program_Error).

One exception to this must-be-same-tag rule is in the case of the equality
operator:

procedure P (O1, O2 : T1'Class) is
begin
   if O1 = O2 then

This will dispatch on the equality operator, if the tags are the same.  If
the tags are different, then obviously the objects can't be the same, so no
exception is raised (thankfully) and the function just returns False.

--------------------------------------------------------------------
Matthew Heaney
Software Development Consultant
<mailto:matthew_heaney@acm.org>
(818) 985-1271




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

* Re: Q: Primitive operation of a type
  1997-07-01  0:00 ` Matthew Heaney
@ 1997-07-02  0:00   ` Mats Weber
  1997-07-03  0:00     ` Matthew Heaney
  0 siblings, 1 reply; 7+ messages in thread
From: Mats Weber @ 1997-07-02  0:00 UTC (permalink / raw)



Matthew Heaney wrote:

> This will dispatch on the tag of the O1 and O2, but only if O1 and O2
> have
> the same tag, which can't be determined until run-time.  If the
> tag-check
> fails, then an exception is raised (I forget which, either
> Constraint_Error
> or Program_Error).

It's Constraint_Error.

> procedure P (O1, O2 : T1'Class) is
> begin
>    if O1 = O2 then
> 
> This will dispatch on the equality operator, if the tags are the same.
> If
> the tags are different, then obviously the objects can't be the same,
> so no
> exception is raised (thankfully) and the function just returns False.

I'm not so sure we should be thankful for this one. In the following
situation, for instance, you may want "=" to return True even it the tags are different:

   type Root_Set is abstract tagged private;

   type AVL_Tree_Set is new Root_Set with private;

   type Boolean_Array_Set is new Root_Set with private;

   A : AVL_Tree_Set;
   B : Boolean_Array_Set;

   procedure P (X, Y : Root_Set'Class) is 
   begin
      if X = Y then ...
   end P;

   P(A, B);

now X => A and Y => B have different tags, but I would like "=" to return True
iff A and B have the same elements, not the same elements and implementation.




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

* Re: Q: Primitive operation of a type
  1997-06-25  0:00 Q: Primitive operation of a type Van Snyder
  1997-07-01  0:00 ` Matthew Heaney
@ 1997-07-02  0:00 ` Martin C. Carlisle
  1 sibling, 0 replies; 7+ messages in thread
From: Martin C. Carlisle @ 1997-07-02  0:00 UTC (permalink / raw)



In article <5oq51h$t7u@netline.jpl.nasa.gov>,
Van Snyder <vsnyder@math.jpl.nasa.gov> wrote:
>I've looked at Barnes's book, and Cohen's book, and I can't
>decide whether it's possible for a procedure to be a primitive
>operation of more than one type.

It is NOT possible for an operation to be primitive for more than
one tagged type.  In essence, you are attempting to do multiple inheritance,
which is not supported directly in Ada 95.  See section 12.4.4.3 of Cohen,
and LRM 3.9.2 (12-13).

For more information on how you might be able to simulate this, see
section 4.6 of the rationale, or you can download a handout I wrote 
regarding this (published in the proceedings of this year's ASEET symposium
at Monmouth Univ).  See http://www.usafa.af.mil/dfcs/papers/#carlisle

--Martin

-- 
Martin C. Carlisle, Computer Science, US Air Force Academy
mcc@cs.usafa.af.mil, http://www.usafa.af.mil/dfcs/bios/carlisle.html
DISCLAIMER:  This content in no way reflects the opinions, standard or 
policy of the US Air Force Academy or the United States Government.




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

* Re: Q: Primitive operation of a type
  1997-07-02  0:00   ` Mats Weber
@ 1997-07-03  0:00     ` Matthew Heaney
  1997-07-08  0:00       ` Mats Weber
  0 siblings, 1 reply; 7+ messages in thread
From: Matthew Heaney @ 1997-07-03  0:00 UTC (permalink / raw)



In article <33BA43CC.E27C69CC@elca-matrix.ch>, Mats.Weber@elca-matrix.ch wrote:


>> procedure P (O1, O2 : T1'Class) is
>> begin
>>    if O1 = O2 then
>> 
>> This will dispatch on the equality operator, if the tags are the same.
>> If
>> the tags are different, then obviously the objects can't be the same,
>> so no
>> exception is raised (thankfully) and the function just returns False.
>
>I'm not so sure we should be thankful for this one. In the following
>situation, for instance, you may want "=" to return True even it the tags
are different:
>
>   type Root_Set is abstract tagged private;
>
>   type AVL_Tree_Set is new Root_Set with private;
>
>   type Boolean_Array_Set is new Root_Set with private;
>
>   A : AVL_Tree_Set;
>   B : Boolean_Array_Set;
>
>   procedure P (X, Y : Root_Set'Class) is 
>   begin
>      if X = Y then ...
>   end P;
>
>   P(A, B);
>
>now X => A and Y => B have different tags, but I would like "=" to return True
>iff A and B have the same elements, not the same elements and implementation.


One technique I like to use is to provide a primitive copy operation and
equality operator that takes an class-wide parameter as an argument:

   type Root_Stack is abstract tagged private;

   procedure Copy (From : in Root_Stack'Class; To : in out Root_Stack);

   function "=" (Left : Root_Stack'Class; Right : Root_Stack) return Boolean;

For most data structures, a copy operation that takes 2 class-wide
parameters is good enough.  Stacks are a pain because you have to populate
the target stack in reverse order of traversal of the source stack.  You
can only really do that if you have access to the representation of the
type, thus the necessity of making copy (for stacks) primitive.

(And you need a Copy operation because you can't do assignment unless the
tags of the left and right hand sides are the same.  So you have to have a
copy operation (and by extension a comparison operation) that you know is
for use with class-wide objects.)

As for equality, I suppose making one (it doesn't matter, Left or Right) of
the specific type at least gives you the opportunity for a more efficient
implementation.  (To be honest, I don't remember if I've done this yet.) 
Of course, you could just make both parameters class-wide too.

There might be an issue with ambiguity, however.  If I did this

procedure P (L, R : Root_Stack'Class) is
begin
   if L = R then

How would the compiler know which operator to call:

function "=" (L, R : Root_Stack) return Boolean;

or 

function "=" (L : Root_Stack'Class; R : Root_Stack) return Boolean;

(See, I told you I haven't done this yet...)

Perhaps a fix is to just use another name for the class-wide version:

function Is_Equal (L : Root_Stack'Class; R : Root_Stack) return Boolean;

So that 

procedure P (L, R : Root_Stack'Class) is
begin
   if Is_Equal (L, R) then 

and we therefore know we're comparing any stack (L) to the specific type of R.

This would appear to be a solution to your set comparison problem.  Each
type that derives from Root_Set overrides the Is_Equal operation so that it
can compare itself to any other set.  (Come to think of it maybe I did do
this...)

To implement this, you'll need a class-wide iterator, so that you can
iterate over any set (or stack or whatever); Is_Equal would use this to
iterate over its Left parameter.  One way to do that is to have factory
method that returns an iterator appropriate for the type (this is
documented in the GOF book), for example

function Is_Equal (L : Root_Stack'Class; R : AVL_Tree_Set) return Boolean is

   The_Iterator : Root_Set_Iterator'Class := New_Iterator (L'Unchecked_Access);
begin
   while not Is_Done (The_Iterator) loop

So the set package would really export 2 tagged types:

package Sets_G is

   type Root_Set is abstract tagged private;

   type Root_Set_Iterator is abstract tagged private;

   function New_Iterator (Set : access Root_Set)
      return Root_Set_Iterator'Class;

   function Is_Done (Iterator : Root_Set_Iterator) return Boolean;
 
   procedure Advance (Iterator : in out Root_Set_Iterator);

   function Is_Equal (L : Root_Set'Class; R : Root_Set) return Boolean;

   procedure Copy (From : Root_Set'Class; To : in out Root_Set);

...

You use the iterator to implement Copy too.

You may have to declare the class-wide parameters (L and From) access
parameters, or perhaps use a named access-to-constant type.  (I really,
really hope I can say

function New_Iterator (Set : access constant Root_Set)
   return Root_Set_Iterator'Class;

in Ada 0X.)

There's another way to do the iteration (I think) using a cursor-based
approach; I haven't tried it yet, but it uses a double-dispatch technique
(I think).

You could even make the iterator indefinate, to force the user to remember
to initialize it:

type Root_Set_Iterator (<>) is abstract tagged private;

I think I have code lying around that does this sort of thing, and I may
have discussed it in one of my posts to the SIGAda Patterns WG list. (Which
I will be getting back to as soon as my new disk drive arrives...)

--------------------------------------------------------------------
Matthew Heaney
Software Development Consultant
<mailto:matthew_heaney@acm.org>
(818) 985-1271




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

* Re: Q: Primitive operation of a type
  1997-07-03  0:00     ` Matthew Heaney
@ 1997-07-08  0:00       ` Mats Weber
  1997-07-14  0:00         ` Matthew Heaney
  0 siblings, 1 reply; 7+ messages in thread
From: Mats Weber @ 1997-07-08  0:00 UTC (permalink / raw)



Matthew Heaney wrote:

> package Sets_G is
> 
>    type Root_Set is abstract tagged private;
> 
>    type Root_Set_Iterator is abstract tagged private;
> 
>    function New_Iterator (Set : access Root_Set)
>       return Root_Set_Iterator'Class;
> 
>    function Is_Done (Iterator : Root_Set_Iterator) return Boolean;
> 
>    procedure Advance (Iterator : in out Root_Set_Iterator);
> 
>    function Is_Equal (L : Root_Set'Class; R : Root_Set) return
> Boolean;
> 
>    procedure Copy (From : Root_Set'Class; To : in out Root_Set);
> 
> ...

How about an iterator that is just a procedure ? That way, the iterator is
part of the primitive profile of the type and gets inherited (and I'll have to
convert all my Ada 83 iterator specifications based on generics :-().
Moreover, it's not always easy to implement an iterator that is a separate
type, e.g. if the structure is implemented as an AVL tree, you have to
remember the path from the root of the tree to the current node, which makes
your iterator less efficent.

generic
   type element_type is private;
package sets is

   type set is abstract tagged private;

   type action_procedure is
      access procedure (element : in element_type);

   procedure enumerate (the_set : in set; 
                        action  : in action_procedure);
   
   ...

end sets;




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

* Re: Q: Primitive operation of a type
  1997-07-08  0:00       ` Mats Weber
@ 1997-07-14  0:00         ` Matthew Heaney
  0 siblings, 0 replies; 7+ messages in thread
From: Matthew Heaney @ 1997-07-14  0:00 UTC (permalink / raw)



In article <33C24D61.86746FC7@elca-matrix.ch>, Mats.Weber@elca-matrix.ch wrote:

>How about an iterator that is just a procedure ? That way, the iterator is
>part of the primitive profile of the type and gets inherited (and I'll have to
>convert all my Ada 83 iterator specifications based on generics :-().

Yes, it's very desirable to make the iteration a primitive operation
somehow, because that admits overriding the operation for the derived type.

However, the procedure solution seems to be fighting the language.  Note 85
in RM95 3.10.2 (37) specifically states that 

"The accessibility rules imply that is is not possible to use the Access
attribute to ... pass a more-nested subprogram as a parameter to a
less-nested subprogram, as might be desired for example for an iterator
abstraction."

So you might not want to convert your Ada 83 generic iterators, because the
next sentance says

"Instead, downward closures can be implemented using generic formal
subprograms."

Of course, if you're using GNAT, you can do what you want by using the
Unrestricted_Access attribute (I think).

If a passive form of iterator is more efficient, then can't we combine the
best of both techniques?

generic
   type Set_Item is private;
package Sets_G is

   type Root_Set is abstract tagged private;

   type Root_Set_Iterator is abstract tagged private;

   procedure Iterate
     (Set         : in        Root_Set; 
      Iterator : in out Root_Set_Iterator'Class) is abstract;

   procedure Process
     (Item      : in Set_Item; 
      Iterator : in Root_Set_Iterator) is abstract;

end;

generic
package Sets_G.AVL_G is
   
   type AVL_Set is new Root_Set with private;

   procedure Iterate
     (Set         : in        AVL_Set;
      Iterator : in out Root_Set_Iterator'Class);
...
end;

package body Sets_G.AVL_G is
 
   procedure Iterate
     (Set         : in        AVL_Set;
      Iterator : in out Root_Set_Iterator'Class) is
   
      <call Process (Item, Iterator) for each item in the avl tree>

   end Iterate;

end;


One issue with this solution is that it would be nice to write a generic
package to do some class-wide operations, but that would require derivation
inside a generic package, with isn't allowed (I think):

generic
   with function Image (Item : Set_Item) return String;
package Sets_G.Text_IO_G is

   procedure Put (Set : in Root_Set'Class);

end;

package body Sets_G.Text_IO_G is

   type Put_Iterator is new Root_Set_Iterator with null record;

   procedure Process
     (Item       : in        Set_Item;
      Iterator : in out Put_Iterator) is
   begin
      Ada.Text_IO.Put (Image (Item));
   end Process;

   procedure Put (Set : in Root_Set'Class) is
       Iterator : Put_Iterator;
   begin
       Iterate (Set, Iterator);
   end;

end;

Sadly, I don't think this will work, because Sets_G.Text_IO_G is a generic
package.

The solution you suggested will would work in this particular case, because
no local data is required to implement Put, so a library level callback can
be passed to the iterate operation.  However, in practice, many
abstractions that use iteration require data local to the subprogram, so a
callback can't be used (portably).

So I'm at a bit of an impass.  If you really want passive iteration, then
you have to use a non-portable attribute.  Or else you can use active
iteration, but at a cost of some loss of efficiency.

Even with the active-iterator-as-separate-type technique, I haven't come
with any good solutions to iterate over a class-wide object.  Perhaps I can
just simplify my life by using a cursor-based solution;  simultaneous
iteration over the same data structure isn't that common, in my experience.

Oh well.  If anyone has any brilliant ideas about iteration (active or
passive) over a class-wide object, please post them!

Matt

--------------------------------------------------------------------
Matthew Heaney
Software Development Consultant
<mailto:matthew_heaney@acm.org>
(818) 985-1271




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

end of thread, other threads:[~1997-07-14  0:00 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1997-06-25  0:00 Q: Primitive operation of a type Van Snyder
1997-07-01  0:00 ` Matthew Heaney
1997-07-02  0:00   ` Mats Weber
1997-07-03  0:00     ` Matthew Heaney
1997-07-08  0:00       ` Mats Weber
1997-07-14  0:00         ` Matthew Heaney
1997-07-02  0:00 ` Martin C. Carlisle

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