comp.lang.ada
 help / color / mirror / Atom feed
* Dispatching and generics - language lawyer question
@ 2002-07-22 23:13 Adam Beneschan
  2002-07-23 15:42 ` Stephen Leake
  0 siblings, 1 reply; 20+ messages in thread
From: Adam Beneschan @ 2002-07-22 23:13 UTC (permalink / raw)


I have a couple questions about what's supposed to happen when a
generic is instantiated with a class-wide type as an actual type.  I'm
hoping that someone who really understands the intricacies of the
language will be kind enough to answer, and provide chapter and verse
from the RM to explain the correct answer.

(1) What is the correct output of the program below?

(2) What would be the correct output if "private" in line [A] is
changed to
    "tagged private"?

I'm pretty sure the answer to (1) is FALSE, since the "=" on line [B]
in the instance denotes the predefined "=" declared for T1 in the
generic, and not the "=" "defined" for the class-wide type, by
8.3(13).  I'm less clear about the answer to (2).  Here, the "="
implicitly declared for T1 is a dispatching operation, and thus should
dispatch if a controlling operand is of a class-wide type (3.9.2(5)),
but it's not clear to me whether the operands in this case are treated
as if they were of the formal type (which is not class-wide) or of the
actual type (which is class-wide).

Thanks in advance for your help.

				-- Adam




generic
    type T1(<>) is private;            -- [A]
package Pak1 is
    type T1_Access is access T1;
    function Check_Equal (X, Y : T1_Access) return boolean;
end Pak1;    

package body Pak1 is
    function Check_Equal (X, Y : T1_Access) return boolean is
    begin
        return X.all = Y.all;          -- [B]
    end Check_Equal;
end Pak1;    

package Pak2 is
    type Root_Type is tagged record
        F1 : Integer;
    end record;
end Pak2;

with Pak2;
package Pak3 is
    type Child is new Pak2.Root_Type with record
        F2 : Integer;
    end record;
    function "=" (Left, Right : Child) return boolean;
end Pak3;    
        
package body Pak3 is
    function "=" (Left, Right : Child) return boolean is
    begin
        return Left.F1 = Right.F1 and then 
               abs Left.F2 = abs Right.F2;
    end "=";
end Pak3;    
        

with Pak1;
with Pak2;
with Pak3;
with Text_IO;
procedure Test62 is
    package Pak1_Inst is new Pak1 (Pak2.Root_Type'Class);
    A1 : Pak1_Inst.T1_Access;
    A2 : Pak1_Inst.T1_Access;
    B  : Boolean;
begin
    A1 := new Pak3.Child' (F1 => 4, F2 => 6);
    A2 := new Pak3.Child' (F1 => 4, F2 => -6);
    B := Pak1_Inst.Check_Equal (A1, A2);
    Text_IO.Put_Line (Boolean'Image (B));
end Test62;



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

* Re: Dispatching and generics - language lawyer question
  2002-07-22 23:13 Dispatching and generics - language lawyer question Adam Beneschan
@ 2002-07-23 15:42 ` Stephen Leake
  2002-07-24 15:37   ` Stephen Leake
  0 siblings, 1 reply; 20+ messages in thread
From: Stephen Leake @ 2002-07-23 15:42 UTC (permalink / raw)


adam@irvine.com (Adam Beneschan) writes:

> I have a couple questions about what's supposed to happen when a
> generic is instantiated with a class-wide type as an actual type.  I'm
> hoping that someone who really understands the intricacies of the
> language will be kind enough to answer, and provide chapter and verse
> from the RM to explain the correct answer.
> 
> (1) What is the correct output of the program below?

GNAT 3.15a1 says "TRUE". Not a definitive answer, but possibly helpful :).

> (2) What would be the correct output if "private" in line [A] is
> changed to "tagged private"?

GNAT 3.15a1 also says "TRUE" for this. 

> I'm pretty sure the answer to (1) is FALSE, since the "=" on line
> [B] in the instance denotes the predefined "=" declared for T1 in
> the generic, and not the "=" "defined" for the class-wide type, by
> 8.3(13). 

I think the controlling paragraph is 12.5.1 (21). I think the behavior
exhibited by GNAT 3.15a1 agrees with this. Note that both variations
of your formal type declaration are "formal private types"; the
presence of "tagged" does not matter here. There is different behavior
for a "formal derived type", which is indicated by the keyword "new".

There is a problem with "reemergence" of predefined operators for
generic formal types, but I don't have an example of when it occurs. I
didn't see a discussion of this in Cohen's "Ada as a Second Language",
either. Nor in the Rationale. But maybe I just didn't look hard
enough.

You might try reading the Rationale for generics; it has some good
stuff in it. It's online at www.adapower.com; click the "resources"
button (I hate html frames!).

-- 
-- Stephe



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

* Re: Dispatching and generics - language lawyer question
@ 2002-07-24  5:33 Grein, Christoph
  2002-07-24 22:55 ` Robert A Duff
  2002-07-25  0:40 ` Robert Dewar
  0 siblings, 2 replies; 20+ messages in thread
From: Grein, Christoph @ 2002-07-24  5:33 UTC (permalink / raw)


> There is a problem with "reemergence" of predefined operators ...

For untagged types, _redefined_ equality does not correctly compose and 
_predefined_ equality reemerges in generics if not properly transferred by a 
formal parameter:

  type X is something;
  function "=" (L, R: X) return Boolean;  -- redefine

  type Y is record
    C: X;
  end record;

  A, B: Y;
  L: Boolean := A = B;  -- here predefined "=" on component C is used, not the
                        -- equality redefined above

  generic
    type T is private;
    --with function "=" (L, R: T) return Boolean is <>;
  package P is ...

  package P_on_X is new P (X);  -- here also predefined "=" on X is used,
                                -- not the equality redefined above

You have to uncomment the generic parameter function to prevent reemergence.

This whole astonishing rule has been introduced because of compatibility with 
Ada 83 (note there were no tagged types in Ada 83 so there reemergence could 
safely be avoided).

In Ada 83, it was not possible to redefine "=" without using a trick (I do not 
remember the exact way to do it, I never did this; you have to use generics with 
the a limited private formal type). When you used this trick to redefine 
equality, also the predefined equality reemerged as in the cases above. Thus for 
compatibility, we have this rule in Ada 95.



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

* Re: Dispatching and generics - language lawyer question
  2002-07-23 15:42 ` Stephen Leake
@ 2002-07-24 15:37   ` Stephen Leake
  0 siblings, 0 replies; 20+ messages in thread
From: Stephen Leake @ 2002-07-24 15:37 UTC (permalink / raw)


Stephen Leake <stephen.a.leake.1@gsfc.nasa.gov> writes:

> There is a problem with "reemergence" of predefined operators for
> generic formal types, but I don't have an example of when it occurs. I
> didn't see a discussion of this in Cohen's "Ada as a Second Language",
> either. Nor in the Rationale. But maybe I just didn't look hard
> enough.

Based on a hint from Christoph Grein, I wrote the following code to
demonstrate the "reemergence problem". It occurs for non-tagged types;
for tagged types, Ada 95 fixed the problem.

 
with Ada.Text_IO; use Ada.Text_IO;
procedure Reemerge
is
   type X is new Integer;
   function "=" (L, R: X) return Boolean;  -- redefine

   function "=" (L, R: X) return Boolean
   is begin
      Put_Line ("in redefined ""="" (X)");
      return Integer (L + R) = 0;
   end "=";

begin
Type_Compose :
    declare
       type Y is record
          C : X;
       end record;

       A : Y := (C => 1);
       B : Y := (C => 1);
    begin
       Put_Line ("Type_Compose");
       Put_Line ("A = B => " & Boolean'Image (A = B));
       -- Here predefined "=" on component C is used, not the
       -- equality redefined above

       New_Line;
       Put_Line ("A.C = B.C => " & Boolean'Image (A.C = B.C));
       -- Here the equality redefined above is used.
    end Type_Compose;

    New_Line;

Generic_Reemerge:
    declare

       generic
          type T is private;
       procedure Compare (L, R : in T);
       -- Print a message with the result of "=" (L, R).

       procedure Compare (L, R : in T)
       is begin
          Put_Line ("L = R => " & Boolean'Image (L = R));
       end Compare;

       procedure Compare_X is new Compare (X);

    begin
       Put_Line ("Generic_Reemerge");
       Compare_X (1, 1);
    end Generic_Reemerge;

    New_Line;
Generic_Correct:
    declare

       generic
          type T is private;
          with function "=" (L, R : in T) return Boolean is <>;
       procedure Compare (L, R : in T);
       -- Print a message with the result of "=" (L, R).

       procedure Compare (L, R : in T)
       is begin
          Put_Line ("L = R => " & Boolean'Image (L = R));
       end Compare;

       procedure Compare_X is new Compare (X);

    begin
       Put_Line ("Generic_Correct");
       Compare_X (1, 1);
    end Generic_Correct;

    New_Line;
Generic_Tagged:
    declare

       package Tagged_Type is
          type Y is tagged record
             Z : Integer;
          end record;

          function "=" (L, R : in Y) return Boolean;
       end Tagged_Type;

       package body Tagged_Type is

          function "=" (L, R : in Y) return Boolean
          is begin
             return L.Z + R.Z = 0;
          end "=";
       end Tagged_Type;

       generic
          type T is private;
       procedure Compare (L, R : in T);
       -- Print a message with the result of "=" (L, R).

       procedure Compare (L, R : in T)
       is begin
          Put_Line ("L = R => " & Boolean'Image (L = R));
       end Compare;

       procedure Compare_Y is new Compare (Tagged_Type.Y);

    begin
       Put_Line ("Generic_Tagged");
       Compare_Y ((Z => 1), (Z => 1));
    end Generic_Tagged;

end Reemerge;

-- 
-- Stephe



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

* Re: Dispatching and generics - language lawyer question
  2002-07-24  5:33 Grein, Christoph
@ 2002-07-24 22:55 ` Robert A Duff
  2002-07-25 15:46   ` Ben Brosgol
  2002-07-25  0:40 ` Robert Dewar
  1 sibling, 1 reply; 20+ messages in thread
From: Robert A Duff @ 2002-07-24 22:55 UTC (permalink / raw)


"Grein, Christoph" <christoph.grein@eurocopter.com> writes:

> For untagged types, _redefined_ equality does not correctly compose and 
> _predefined_ equality reemerges in generics if not properly transferred by a 
> formal parameter:

What do you think the rule *should* be?  It does seem bad that
predefined "=" reemerges.  And of course the rule for "/=" should be the
same as for "=".

What about "<"?  Should the predefined one reemerge?  If a generic
sorting package calls the primitive "<" of the generic formal type,
it would seem desirable to use the user-defined one, rather than the
predefined one.

But what about "+" and "mod" and so forth?  Package Text_IO.Integer_IO
probably uses the predefined "mod" to format the number as a string.
But the *spec* of that package doesn't say that -- it just says the
string is formatted according to some rules.  If the predefined "mod"
did *not* reemerge, then a user-defined "mod" operator would break
Text_IO.Integer_IO.  You may say, "tough luck; that's the programmer's
fault".  But how should the semantics of Integer_IO be defined in the
RM?  Surely the use of "mod" there is an implementation detail.

- Bob



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

* Re: Dispatching and generics - language lawyer question
  2002-07-24  5:33 Grein, Christoph
  2002-07-24 22:55 ` Robert A Duff
@ 2002-07-25  0:40 ` Robert Dewar
  1 sibling, 0 replies; 20+ messages in thread
From: Robert Dewar @ 2002-07-25  0:40 UTC (permalink / raw)


"Grein, Christoph" <christoph.grein@eurocopter.com> wrote in message news:<mailman.1027489082.2047.comp.lang.ada@ada.eu.org>...
> When you used this trick to redefine 
> equality, also the predefined equality reemerged as in 
> the cases above. Thus for 
> compatibility, we have this rule in Ada 95.

The compatibility consideration here does not relate solely
to the use of the trick for redefining equality.



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

* Re: Dispatching and generics - language lawyer question
  2002-07-24 22:55 ` Robert A Duff
@ 2002-07-25 15:46   ` Ben Brosgol
  2002-07-29 20:38     ` Robert A Duff
  0 siblings, 1 reply; 20+ messages in thread
From: Ben Brosgol @ 2002-07-25 15:46 UTC (permalink / raw)


> > For untagged types, _redefined_ equality does not correctly compose and
> > _predefined_ equality reemerges in generics if not properly transferred
by a
> > formal parameter:
>
> But what about "+" and "mod" and so forth?  Package Text_IO.Integer_IO
> probably uses the predefined "mod" to format the number as a string.
> But the *spec* of that package doesn't say that -- it just says the
> string is formatted according to some rules.  If the predefined "mod"
> did *not* reemerge, then a user-defined "mod" operator would break
> Text_IO.Integer_IO.  You may say, "tough luck; that's the programmer's
> fault".  But how should the semantics of Integer_IO be defined in the
> RM?  Surely the use of "mod" there is an implementation detail.

As another interesting case, suppose that you declare an integer type T with
"mod" specified as abstract.  If "mod" on the formal type is invoked from
the generic body, then either the instantiation Ada.Text_IO.Integer_IO(T)
would need to be rejected (a rather flagrant violation of the "contract
model") or else the predefined "mod" would need to reemerge.






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

* Re: Dispatching and generics - language lawyer question
  2002-07-25 15:46   ` Ben Brosgol
@ 2002-07-29 20:38     ` Robert A Duff
  2002-07-31 22:52       ` Dmitry A.Kazakov
  0 siblings, 1 reply; 20+ messages in thread
From: Robert A Duff @ 2002-07-29 20:38 UTC (permalink / raw)


Hi, Ben.

"Ben Brosgol" <brosgol@world.std.com> writes:

> As another interesting case, suppose that you declare an integer type T with
> "mod" specified as abstract.  If "mod" on the formal type is invoked from
> the generic body, then either the instantiation Ada.Text_IO.Integer_IO(T)
> would need to be rejected (a rather flagrant violation of the "contract
> model") or else the predefined "mod" would need to reemerge.

Yes, that *is* an interesting case, which I hadn't thought of.

Note that for tagged types, the generic declares whether it wants to
accept types with abstract primitive ops, so the instantiation can be
rejected without violating the contract model.  It's uncomfortable that
there are misc. rules like this that distinguish tagged types from
untagged types.  Could we do better if designing the language from
scratch?  (I think many such rules are needed for compatibility with Ada
83.)  I'm not sure what the right answer is in this case.

I'm disappointed that nobody answered my previous question -- what
should the rules about predefined operators in generics be?  I don't
like the current Ada rules (which require reemergence of the "wrong"
operator), but the alternatives are uncomfortable in various ways.
Christoph Grein, or anybody else want to comment?

Here's an interesting case: a generic Sort routine, that takes "<" as a
parameter.  (Or it could be called "Less_Than" -- it doesn't matter what
it's called, or whether it's an operator symbol.)  The Sort routine
might require that the "<" behave in certain ways (like if A<B and B<C
both return True, then A<C should always return True).  Is there any way
to specify that (other than via comments)?  Is there some way to design
the language so that the generic Sort could formally require such
properties of "<"?

This is similar to the Text_IO.Integer_IO example, where the code wants
to assume that "mod" behaves according to some mathematical rules.
(I'm not even sure how to state those rules precisely.)

- Bob



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

* Re: Dispatching and generics - language lawyer question
  2002-07-31 22:52       ` Dmitry A.Kazakov
@ 2002-07-31 20:18         ` Robert A Duff
  2002-08-02  1:15           ` Dmitry A.Kazakov
  2002-08-13 22:50           ` Randy Brukardt
  0 siblings, 2 replies; 20+ messages in thread
From: Robert A Duff @ 2002-07-31 20:18 UTC (permalink / raw)


Dmitry A.Kazakov <mailbox@dmitry-kazakov.de> writes:

> [away from Ada 95] There should be only tagged types and no untagged ones. 
> Then I suppose the formal derived types will do the whole work, because the 
> set of all available operations will be defined by the root type. All types 
> tagged means only that T'Class exists for all but class-wide types. The tag 
> should be kept in the instances of T'Class and class-wide pointers, but not 
> in the values of T, so no performance hit when objects are specific.

This is a reasonable idea (not new -- I and probably others have thought
of it before).

As you imply, there are two ways to implement tags: (1) Store the tag
with each object.  Then an access value (or a pass-by-reference
parameter) can be represented as the address of the object.  Or (2),
do not store the tag with each object, but gin it up when necessary.
This requires access values and parameters to be a pair -- the tag, plus
the address of the object.  When you do X'Access, the compiler creates
the pair.

Neither method is uniformly better: there are some programs that will be
more efficient with method (1), and some more efficient with method (2).
This indicates that the user should have the option.  I would suggest
the default (in this hypothetical language) should be (2), and a
per-type pragma could request (1) for that type and its descendants.
This preserves the Bauer Principle -- if you never say 'Class, you never
have any overhead for storing or manipulating tags at run time.

Of course, having options like that complicates compilers.

For most of the Ada 9X design, either (1) or (2) were feasible (for
tagged types).  However, a last-minute change to the language made (2)
infeasible.  Namely, the fact that all tagged parameters are aliased.

As far as I know, all existing compilers use (1).

Note that the distinction between (1) and (2) is analogous to the
compiler storing array bounds with the array, or with pointers to the
array.  Neither one is uniformly better.  I believe GNAT allows a user
choice, and I think all other compilers store bounds with the array
(analogous to (1)).

> > I'm disappointed that nobody answered my previous question -- what
> > should the rules about predefined operators in generics be?  I don't
> > like the current Ada rules (which require reemergence of the "wrong"
> > operator), but the alternatives are uncomfortable in various ways.
> > Christoph Grein, or anybody else want to comment?
> > 
> > Here's an interesting case: a generic Sort routine, that takes "<" as a
> > parameter.  (Or it could be called "Less_Than" -- it doesn't matter what
> > it's called, or whether it's an operator symbol.)  The Sort routine
> > might require that the "<" behave in certain ways (like if A<B and B<C
> > both return True, then A<C should always return True).  Is there any way
> > to specify that (other than via comments)?  Is there some way to design
> > the language so that the generic Sort could formally require such
> > properties of "<"?
> 
> [far away form Ada 95] You should simply have different root types. 
> Transitive_Ordered should be a[!] root type for "all" types with transitive 
> "<". "All" means the types you want to pass the actual parameter. Of course 
> then the language should allow creating supertypes on-fly, because one 
> cannot foresee all possible root types like Ada tries with its 
> classification of formal types. Let Integer is not a descendant of 
> Transitive_Ordered. Then one could create a "proxy" type which is 
> simultaneously a supertype of Integer and a subtype of Transitive_Ordered. 
> [The compiler should check that "<" be implemented.] So Integer would 
> become a subtype Transitive_Ordered in the given context and thus could be 
> used as an actual parameter of the generic. This of course would require MI 
> and dynamically maintained dispatch tables.

Again, good ideas.  But they don't solve the problem I was thinking of.

By the way, I don't see why you need dynamically maintained dispatch
tables.  To me, "on the fly" means "somewhere later in the code" --
i.e. in some module other than where the type is first declared.
That need not imply "at run time", I think.

The notion of dynamically maintained dispatch tables scares me: I hope
that "<" on type T does not dispatch to different subprogram bodies
at different times (for the same type tag)!

It seems like adding new operations to a type should only be allowed at
the same nesting level as the original type declaration.  I shudder to
think of the run-time overhead of adding an operation in a recursive
procedure.  ("Adding new op" is essentially the same thing as "adding a
new parent type", here.)

But anyway, as you explain below, none of this solves the problem.  The
compiler checks that "<" exists with the right parameter types, but that
doesn't *prove* its transitive (whether or not the Transitive_Ordered
idea is used).

> > This is similar to the Text_IO.Integer_IO example, where the code wants
> > to assume that "mod" behaves according to some mathematical rules.
> > (I'm not even sure how to state those rules precisely.)
> 
> Well, this is about LSP. No one can enforce semantics of an operation. 

Exactly my point.  For *this* issue, all of the above good ideas are
equivalent to simply changing the language so that predefined operators
do not reemerge.

> However, the difference between what we have in Ada now and what I would 
> like to have, is that presently operations of a private formal type are 
> matched by their profiles. This warranties absolutely nothing.

Right, it warranties nothing.

>... With generic 
> derived types, you would have a subtype relation which assumes a 
> meaningfull implementation of the root type operations.

But this also warranties nothing.  One must "assume" that operations do
what they're supposed to do; the compiler can't prove it.  That's no
different from the current case, where "mod" could be redefined to do
something weird, thus breaking Text_IO.Integer_IO (I mean, if we changed
the rules to avoid the reemergence).

You still haven't answered my question: What should the RM say that
Integer_IO.Put does?  (Or if it were user-defined, what should the
comment on its spec say?)  I don't care whether the language removes
reemergence, or all your nice ideas above, including about formal
derived types, are adopted -- either way, it's difficult to define the
semantics of Put if it might be calling some user-defined version of
"mod".

The current RM doesn't even mention that Put calls "mod".  In fact,
there are various ways of implementing Put, some of which call "mod" and
some of which don't.  Surely, Put calling "mod" is an implementation
detail?

- Bob



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

* Re: Dispatching and generics - language lawyer question
  2002-07-29 20:38     ` Robert A Duff
@ 2002-07-31 22:52       ` Dmitry A.Kazakov
  2002-07-31 20:18         ` Robert A Duff
  0 siblings, 1 reply; 20+ messages in thread
From: Dmitry A.Kazakov @ 2002-07-31 22:52 UTC (permalink / raw)


Robert A Duff wrote:

> "Ben Brosgol" <brosgol@world.std.com> writes:
> 
>> As another interesting case, suppose that you declare an integer type T
>> with
>> "mod" specified as abstract.  If "mod" on the formal type is invoked from
>> the generic body, then either the instantiation Ada.Text_IO.Integer_IO(T)
>> would need to be rejected (a rather flagrant violation of the "contract
>> model") or else the predefined "mod" would need to reemerge.
> 
> Yes, that *is* an interesting case, which I hadn't thought of.
> 
> Note that for tagged types, the generic declares whether it wants to
> accept types with abstract primitive ops, so the instantiation can be
> rejected without violating the contract model.  It's uncomfortable that
> there are misc. rules like this that distinguish tagged types from
> untagged types.  Could we do better if designing the language from
> scratch?  (I think many such rules are needed for compatibility with Ada
> 83.)  I'm not sure what the right answer is in this case.

[away from Ada 95] There should be only tagged types and no untagged ones. 
Then I suppose the formal derived types will do the whole work, because the 
set of all available operations will be defined by the root type. All types 
tagged means only that T'Class exists for all but class-wide types. The tag 
should be kept in the instances of T'Class and class-wide pointers, but not 
in the values of T, so no performance hit when objects are specific.

> I'm disappointed that nobody answered my previous question -- what
> should the rules about predefined operators in generics be?  I don't
> like the current Ada rules (which require reemergence of the "wrong"
> operator), but the alternatives are uncomfortable in various ways.
> Christoph Grein, or anybody else want to comment?
> 
> Here's an interesting case: a generic Sort routine, that takes "<" as a
> parameter.  (Or it could be called "Less_Than" -- it doesn't matter what
> it's called, or whether it's an operator symbol.)  The Sort routine
> might require that the "<" behave in certain ways (like if A<B and B<C
> both return True, then A<C should always return True).  Is there any way
> to specify that (other than via comments)?  Is there some way to design
> the language so that the generic Sort could formally require such
> properties of "<"?

[far away form Ada 95] You should simply have different root types. 
Transitive_Ordered should be a[!] root type for "all" types with transitive 
"<". "All" means the types you want to pass the actual parameter. Of course 
then the language should allow creating supertypes on-fly, because one 
cannot foresee all possible root types like Ada tries with its 
classification of formal types. Let Integer is not a descendant of 
Transitive_Ordered. Then one could create a "proxy" type which is 
simultaneously a supertype of Integer and a subtype of Transitive_Ordered. 
[The compiler should check that "<" be implemented.] So Integer would 
become a subtype Transitive_Ordered in the given context and thus could be 
used as an actual parameter of the generic. This of course would require MI 
and dynamically maintained dispatch tables.

> This is similar to the Text_IO.Integer_IO example, where the code wants
> to assume that "mod" behaves according to some mathematical rules.
> (I'm not even sure how to state those rules precisely.)

Well, this is about LSP. No one can enforce semantics of an operation. 
However, the difference between what we have in Ada now and what I would 
like to have, is that presently operations of a private formal type are 
matched by their profiles. This warranties absolutely nothing. With generic 
derived types, you would have a subtype relation which assumes a 
meaningfull implementation of the root type operations.

-- 
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Dispatching and generics - language lawyer question
  2002-08-02  1:15           ` Dmitry A.Kazakov
@ 2002-08-01 16:30             ` Hyman Rosen
  2002-08-02 23:42               ` Dmitry A.Kazakov
  0 siblings, 1 reply; 20+ messages in thread
From: Hyman Rosen @ 2002-08-01 16:30 UTC (permalink / raw)


Dmitry A.Kazakov wrote:
> I think that (1) would cause many problems with MI. When tag is a part of 
> the value and then view conversions are problematic.
> There is also redispatch which is IMO incompatible with (1). I think 
> redispatch must be removed, because when object identification is really 
> necessary (to redispatch) one could use a solution similar to J.-P. Rosen 
> trick.

C++ uses (1), storing multiple "tags" (actually, virtual
table pointers) in the object if necessary, and supports
redispatch just fine. The scheme used by the new version
of g++ is described at
<http://www.codesourcery.com/cxx-abi/abi.html>.

I doubt that the language designers, assuming they do add
MI to Ada, would be so silly as to deliberately choose to
implement it in a way that could never be compatible with
C++ object layout.




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

* Re: Dispatching and generics - language lawyer question
  2002-07-31 20:18         ` Robert A Duff
@ 2002-08-02  1:15           ` Dmitry A.Kazakov
  2002-08-01 16:30             ` Hyman Rosen
  2002-08-13 22:50           ` Randy Brukardt
  1 sibling, 1 reply; 20+ messages in thread
From: Dmitry A.Kazakov @ 2002-08-02  1:15 UTC (permalink / raw)


Robert A Duff wrote:

> Dmitry A.Kazakov <mailbox@dmitry-kazakov.de> writes:
> 
>> [away from Ada 95] There should be only tagged types and no untagged
>> [ones.
>> Then I suppose the formal derived types will do the whole work, because
>> the set of all available operations will be defined by the root type. All
>> types tagged means only that T'Class exists for all but class-wide types.
>> The tag should be kept in the instances of T'Class and class-wide
>> pointers, but not in the values of T, so no performance hit when objects
>> are specific.
> 
> This is a reasonable idea (not new -- I and probably others have thought
> of it before).
> 
> As you imply, there are two ways to implement tags: (1) Store the tag
> with each object.  Then an access value (or a pass-by-reference
> parameter) can be represented as the address of the object.  Or (2),
> do not store the tag with each object, but gin it up when necessary.
> This requires access values and parameters to be a pair -- the tag, plus
> the address of the object.  When you do X'Access, the compiler creates
> the pair.
> 
> Neither method is uniformly better: there are some programs that will be
> more efficient with method (1), and some more efficient with method (2).
> This indicates that the user should have the option.  I would suggest
> the default (in this hypothetical language) should be (2), and a
> per-type pragma could request (1) for that type and its descendants.
> This preserves the Bauer Principle -- if you never say 'Class, you never
> have any overhead for storing or manipulating tags at run time.
> 
> Of course, having options like that complicates compilers.

I think that (1) would cause many problems with MI. When tag is a part of 
the value and then view conversions are problematic.

> For most of the Ada 9X design, either (1) or (2) were feasible (for
> tagged types).  However, a last-minute change to the language made (2)
> infeasible.  Namely, the fact that all tagged parameters are aliased.

There is also redispatch which is IMO incompatible with (1). I think 
redispatch must be removed, because when object identification is really 
necessary (to redispatch) one could use a solution similar to J.-P. Rosen 
trick.

> As far as I know, all existing compilers use (1).
> 
> Note that the distinction between (1) and (2) is analogous to the
> compiler storing array bounds with the array, or with pointers to the
> array.  Neither one is uniformly better.  I believe GNAT allows a user
> choice, and I think all other compilers store bounds with the array
> (analogous to (1)).

Yes. Same with discriminants. I have only a vague idea, that there should 
be way to separate all things like that from values. The goal is of course 
to make arrays, strings etc user-defined types without additional overhead. 
Then of course you should be able to derive from an abstract array and 
provide an implementation which is internally not an array at all. So the 
client will never know.

>> > I'm disappointed that nobody answered my previous question -- what
>> > should the rules about predefined operators in generics be?  I don't
>> > like the current Ada rules (which require reemergence of the "wrong"
>> > operator), but the alternatives are uncomfortable in various ways.
>> > Christoph Grein, or anybody else want to comment?
>> > 
>> > Here's an interesting case: a generic Sort routine, that takes "<" as a
>> > parameter.  (Or it could be called "Less_Than" -- it doesn't matter
>> > what
>> > it's called, or whether it's an operator symbol.)  The Sort routine
>> > might require that the "<" behave in certain ways (like if A<B and B<C
>> > both return True, then A<C should always return True).  Is there any
>> > way
>> > to specify that (other than via comments)?  Is there some way to design
>> > the language so that the generic Sort could formally require such
>> > properties of "<"?
>> 
>> [far away form Ada 95] You should simply have different root types.
>> Transitive_Ordered should be a[!] root type for "all" types with
>> transitive "<". "All" means the types you want to pass the actual
>> parameter. Of course then the language should allow creating supertypes
>> on-fly, because one cannot foresee all possible root types like Ada tries
>> with its classification of formal types. Let Integer is not a descendant
>> of Transitive_Ordered. Then one could create a "proxy" type which is
>> simultaneously a supertype of Integer and a subtype of
>> Transitive_Ordered.
>> [The compiler should check that "<" be implemented.] So Integer would
>> become a subtype Transitive_Ordered in the given context and thus could
>> be used as an actual parameter of the generic. This of course would
>> require MI and dynamically maintained dispatch tables.
> 
> Again, good ideas.  But they don't solve the problem I was thinking of.
> 
> By the way, I don't see why you need dynamically maintained dispatch
> tables.  To me, "on the fly" means "somewhere later in the code" --
> i.e. in some module other than where the type is first declared.
> That need not imply "at run time", I think.
> 
> The notion of dynamically maintained dispatch tables scares me: I hope
> that "<" on type T does not dispatch to different subprogram bodies
> at different times (for the same type tag)!
>
> It seems like adding new operations to a type should only be allowed at
> the same nesting level as the original type declaration.  I shudder to
> think of the run-time overhead of adding an operation in a recursive
> procedure.  ("Adding new op" is essentially the same thing as "adding a
> new parent type", here.)

Yes, I also do not think that one could override in any context without a 
heavy penalty. I think if that would be allowed then no expression would be 
static when used in a subprogram. No, I meant another thing. If a derived 
type is created in some nested context and overrides, say "<", then the 
dispatching table of "<" should be modified once when the type is 
elaborated and restored when the context is finalized.

> But anyway, as you explain below, none of this solves the problem.  The
> compiler checks that "<" exists with the right parameter types, but that
> doesn't *prove* its transitive (whether or not the Transitive_Ordered
> idea is used).
> 
>> > This is similar to the Text_IO.Integer_IO example, where the code wants
>> > to assume that "mod" behaves according to some mathematical rules.
>> > (I'm not even sure how to state those rules precisely.)
>> 
>> Well, this is about LSP. No one can enforce semantics of an operation.
> 
> Exactly my point.  For *this* issue, all of the above good ideas are
> equivalent to simply changing the language so that predefined operators
> do not reemerge.

One should distinguish overloading and overriding. If ">" is overridden 
then a reemergence would clearly violate LSP. On contrary in the case of 
overloading reemergence would enforce LSP. So logically, the reemergency 
rule is OK so far the type is untagged [but there should be no such (:-))]. 
But look at this:

package Some_Type is
   type X is tagged null record;
   function "<" (A, B : X) return Boolean;
end Some_Type;

package body Some_Type is
   function "<" (A, B : X) return Boolean is
   begin
      return True;
   end "<";
end Some_Type;

with Ada.Text_IO; use Ada.Text_IO;
with Some_Type;   use Some_Type;

procedure Test is
   XX : X;

   generic procedure Compare (A : X);

   procedure Compare (A : X) is
   begin
      if A < A then
         Put_Line ("Reemerged");
      else
         Put_Line ("Overloaded");
      end if;
   end Compare;

   procedure G1 is new Compare;
   procedure N1 (A : X) is
   begin
      if A < A then
         Put_Line ("Reemerged");
      else
         Put_Line ("Overloaded");
      end if;
   end N1;

   function "<" (A, B : X) return Boolean is
   begin
      return False;
   end "<";

   procedure G2 is new Compare;
   procedure N2 (A : X) is
   begin
      if A < A then
         Put_Line ("Reemerged");
      else
         Put_Line ("Overloaded");
      end if;
   end N2;

begin
   G1 (XX); -- Reemerged
   N1 (XX); -- Reemerged
   G2 (XX); -- Reemerged
   N2 (XX); -- Overloaded
end Test;

Is THIS OK? I do not think so. The first thought is to prohibit overloading 
of primitive operations.

>> However, the difference between what we have in Ada now and what I would
>> like to have, is that presently operations of a private formal type are
>> matched by their profiles. This warranties absolutely nothing.
> 
> Right, it warranties nothing.
> 
>>... With generic
>> derived types, you would have a subtype relation which assumes a
>> meaningfull implementation of the root type operations.
> 
> But this also warranties nothing.  One must "assume" that operations do
> what they're supposed to do; the compiler can't prove it.

To some extent it could. Let we introduce, as many proposed, pre-, 
post-conditions and invariants. Then some of requirements on operations of 
Transitive_Order could be expressed in their terms. Then (do not ask me 
how) the compiler could check that a pre-condition of a derived type 
declared to be a "strong subtype" is not stronger than it was for the base 
type etc.

This will still warranty nothing, but it is much like 
Unchecked_Deallocation. How do you warranty that it is not called twice? 
One thing is when the compiler silently uses ">", just because it has a 
right name and profile. Another thing is when the compiler says: sorry guy, 
">" is abstract, please, override it, even if the overriding would be a 
simple renaming, even if some idiot would call a TRAP instruction from the 
overriding.

> That's no
> different from the current case, where "mod" could be redefined to do
> something weird, thus breaking Text_IO.Integer_IO (I mean, if we changed
> the rules to avoid the reemergence).
> 
> You still haven't answered my question: What should the RM say that
> Integer_IO.Put does?  (Or if it were user-defined, what should the
> comment on its spec say?)  I don't care whether the language removes
> reemergence, or all your nice ideas above, including about formal
> derived types, are adopted -- either way, it's difficult to define the
> semantics of Put if it might be calling some user-defined version of
> "mod".

The precondition of Integer_IO is that the generic parameter is of some 
integer type. It means that all what is said about integer types in RM 
should be true for it. RM could say that for an integer type for any x and 
any y/=0 there are d and r of the type that x=y*d+r and etc (*). There is a 
related question: how to make preconditions as weak as possible? 
Definitely, Put does not need *all* integer operations. How to express 
that? To refine further the type hierarchy like with Transitive_Order would 
be a heavy burden for a programmer, because then he/she should instantiate 
each procedure of the package separately depending on what subset of 
operations (subtype) the procedure require.

(*) You can always write in RM: "it is a bounded error if" and a draconian 
requirement follows (:-))

Also any polymorphic solution (generic or class-wide) may face 
substitutability problems because of overriding. Thus for simple things 
like Integer_IO it could be better to have a non-polymorphic one. I mean 
user-defined type conversions and subtyping through conversions. In fact we 
do it manually on daily basis when write something like:

type X is range ...;
A : X;

Put (Integer'Image (Integer (A)));

Let we have some integer type Int_Out which cannot have instances 
(variables). Then let's declare Put on it. Then if I would like to make an 
output for X, I would declare X a subtype of IO_Int by providing a 
conversion X->Int_Out. The compiler would apply the conversion should I 
write Put (X). This suffers no substitutability problems (**), because Put 
will always deal with an Int_Out.

(**) If Int_Out is capable to represent all values of the derived type.

> The current RM doesn't even mention that Put calls "mod".  In fact,
> there are various ways of implementing Put, some of which call "mod" and
> some of which don't.  Surely, Put calling "mod" is an implementation
> detail?

Yes. Yet it is consistent, because what is not an implementation detail is 
that Put *may* call "mod" expecting a meaningfull behaviour from it. 

-- 
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Dispatching and generics - language lawyer question
  2002-08-02 23:42               ` Dmitry A.Kazakov
@ 2002-08-02 15:49                 ` Hyman Rosen
  2002-08-02 17:48                   ` Stephen Leake
                                     ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Hyman Rosen @ 2002-08-02 15:49 UTC (permalink / raw)


Dmitry A.Kazakov wrote:
> So in C++ int will never be a class, worse to C++.

It's never going be a class in Ada either.

 > I doubt that it is worth effors to keep objects Ada
 > and C++ compatible.

I think that the GNAT creators would disagree with you.
As far as I know, they make corresponding Ada and C++
objects layout compatible. Perhaps one of them will
explain why.




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

* Re: Dispatching and generics - language lawyer question
  2002-08-02 15:49                 ` Hyman Rosen
@ 2002-08-02 17:48                   ` Stephen Leake
  2002-08-10  3:03                     ` Warren W. Gay VE3WWG
  2002-08-05 11:15                   ` Dmitry A. Kazakov
  2002-08-12 12:44                   ` Robert Dewar
  2 siblings, 1 reply; 20+ messages in thread
From: Stephen Leake @ 2002-08-02 17:48 UTC (permalink / raw)


Hyman Rosen <hyrosen@mail.com> writes:

> Dmitry A.Kazakov wrote:
> > So in C++ int will never be a class, worse to C++.
> 
> It's never going be a class in Ada either.
> 
>  > I doubt that it is worth effors to keep objects Ada
>  > and C++ compatible.
> 
> I think that the GNAT creators would disagree with you.
> As far as I know, they make corresponding Ada and C++
> objects layout compatible. Perhaps one of them will
> explain why.

It allows you to derive a new Ada type from a C++ class, and vice
versa. Then a dispatching call from a C++ routine can call an Ada
body, and vice versa.

It allows _full_ mixed language programming with Ada and C++. This is
a _very_ good feature. The alternative requires a C interface between
C++ and Ada, which is simply not as good.

-- 
-- Stephe



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

* Re: Dispatching and generics - language lawyer question
  2002-08-01 16:30             ` Hyman Rosen
@ 2002-08-02 23:42               ` Dmitry A.Kazakov
  2002-08-02 15:49                 ` Hyman Rosen
  0 siblings, 1 reply; 20+ messages in thread
From: Dmitry A.Kazakov @ 2002-08-02 23:42 UTC (permalink / raw)


Hyman Rosen wrote:

> Dmitry A.Kazakov wrote:
>> I think that (1) would cause many problems with MI. When tag is a part of
>> the value and then view conversions are problematic.
>> There is also redispatch which is IMO incompatible with (1). I think
>> redispatch must be removed, because when object identification is really
>> necessary (to redispatch) one could use a solution similar to J.-P. Rosen
>> trick.
> 
> C++ uses (1), storing multiple "tags" (actually, virtual
> table pointers) in the object if necessary, and supports
> redispatch just fine. The scheme used by the new version
> of g++ is described at
> <http://www.codesourcery.com/cxx-abi/abi.html>.

So in C++ int will never be a class, worse to C++.
 
> I doubt that the language designers, assuming they do add
> MI to Ada, would be so silly as to deliberately choose to
> implement it in a way that could never be compatible with
> C++ object layout.

I do not think that to repeat others faults is a good idea. As long as we 
have no C++ operating system APIs (shudder), I doubt that it is worth 
effors to keep objects Ada and C++ compatible. To communicate with C++ one 
could declare a simple record type with fields reserved for places where 
vtptrs are.

-- 
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Dispatching and generics - language lawyer question
  2002-08-02 15:49                 ` Hyman Rosen
  2002-08-02 17:48                   ` Stephen Leake
@ 2002-08-05 11:15                   ` Dmitry A. Kazakov
  2002-08-12 12:44                   ` Robert Dewar
  2 siblings, 0 replies; 20+ messages in thread
From: Dmitry A. Kazakov @ 2002-08-05 11:15 UTC (permalink / raw)


On Fri, 02 Aug 2002 11:49:34 -0400, Hyman Rosen <hyrosen@mail.com>
wrote:

>Dmitry A.Kazakov wrote:
>> So in C++ int will never be a class, worse to C++.
>
>It's never going be a class in Ada either.

It was rather about an imaginary successor of Ada, which should not be
Ada++ in the sense of C++ = "C with classes". At some point one will
have to drive the line.

> > I doubt that it is worth effors to keep objects Ada
> > and C++ compatible.
>
>I think that the GNAT creators would disagree with you.
>As far as I know, they make corresponding Ada and C++
>objects layout compatible. Perhaps one of them will
>explain why.

Maybe because they get it more or less free. (:-))

---
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Dispatching and generics - language lawyer question
  2002-08-02 17:48                   ` Stephen Leake
@ 2002-08-10  3:03                     ` Warren W. Gay VE3WWG
  0 siblings, 0 replies; 20+ messages in thread
From: Warren W. Gay VE3WWG @ 2002-08-10  3:03 UTC (permalink / raw)


Stephen Leake wrote:
> Hyman Rosen <hyrosen@mail.com> writes:
>>Dmitry A.Kazakov wrote:
>>>So in C++ int will never be a class, worse to C++.
>>
>>It's never going be a class in Ada either.
>>
>> > I doubt that it is worth effors to keep objects Ada
>> > and C++ compatible.
>>
>>I think that the GNAT creators would disagree with you.
>>As far as I know, they make corresponding Ada and C++
>>objects layout compatible. Perhaps one of them will
>>explain why.
> 
> It allows you to derive a new Ada type from a C++ class, and vice
> versa. Then a dispatching call from a C++ routine can call an Ada
> body, and vice versa.
> 
> It allows _full_ mixed language programming with Ada and C++. This is
> a _very_ good feature. The alternative requires a C interface between
> C++ and Ada, which is simply not as good.

At least at the level of GNAT 3.13p, the C++ support was not quite
"full". To the best of my knowledge, the C++ destructors did
not work when the object went out of scope within Ada code.

You could call the destructor manually, but this of course is
not as convenient as the Ada.Finalization.Controlled type of
object that GNAT does support finalization for.

So in this respect, unless they have fixed this in later
versions (I did not check), the C++ level of support is not yet
"full" in GNAT.

Warren.




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

* Re: Dispatching and generics - language lawyer question
  2002-08-02 15:49                 ` Hyman Rosen
  2002-08-02 17:48                   ` Stephen Leake
  2002-08-05 11:15                   ` Dmitry A. Kazakov
@ 2002-08-12 12:44                   ` Robert Dewar
  2 siblings, 0 replies; 20+ messages in thread
From: Robert Dewar @ 2002-08-12 12:44 UTC (permalink / raw)


Hyman Rosen <hyrosen@mail.com> wrote in message news:<1028303374.179416@master.nyc.kbcfp.com>...

> I think that the GNAT creators would disagree with you.
> As far as I know, they make corresponding Ada and C++
> objects layout compatible. Perhaps one of them will
> explain why.


That's a bit confused. The layout of tagged types in
GNAT is defined by the body of the run-time unit Ada.Tags.
Whether this corresponds or not to some particular C++
compiler depends on whether you tailor Ada.Tags
appropriately. 

In practice, the only significant use of the tight C++
binding of objects and tagged types in GNAT was by SGI
for some of their graphics packages, otherwise I don't
think the feature has been used. 

(there are lots of things in GNAT that are very interesting
but don't get used, e.g. the entire Information Systems
Annex :-)



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

* Re: Dispatching and generics - language lawyer question
  2002-07-31 20:18         ` Robert A Duff
  2002-08-02  1:15           ` Dmitry A.Kazakov
@ 2002-08-13 22:50           ` Randy Brukardt
  2002-08-14  0:02             ` Robert A Duff
  1 sibling, 1 reply; 20+ messages in thread
From: Randy Brukardt @ 2002-08-13 22:50 UTC (permalink / raw)


Robert A Duff wrote in message ...
Bon Duff wrote:

>Note that the distinction between (1) and (2) is analogous to the
>compiler storing array bounds with the array, or with pointers to the
>array.  Neither one is uniformly better.  I believe GNAT allows a user
>choice, and I think all other compilers store bounds with the array
>(analogous to (1)).


Janus/Ada actually does neither -- the bounds are stored separately, and
belong to neither. (The memory used is allocated separately if needed.)
Indeed, I don't think you can store the bounds with the array for
slices, so I think your analogy is incorrect (there is nothing like
slices for tagged types, of course).

           Randy.






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

* Re: Dispatching and generics - language lawyer question
  2002-08-13 22:50           ` Randy Brukardt
@ 2002-08-14  0:02             ` Robert A Duff
  0 siblings, 0 replies; 20+ messages in thread
From: Robert A Duff @ 2002-08-14  0:02 UTC (permalink / raw)


"Randy Brukardt" <randy@rrsoftware.com> writes:

> Janus/Ada actually does neither -- the bounds are stored separately, and
> belong to neither. (The memory used is allocated separately if needed.)
> Indeed, I don't think you can store the bounds with the array for
> slices, so I think your analogy is incorrect (there is nothing like
> slices for tagged types, of course).

Good point.  If a slice is passed as a parameter, the bounds ought to be
passed separately from the pointer-to-data.  In Ada 83, I suppose one
could always copy slices, but in Ada 95, some types require pass by
reference.  Including a slice of one of those.

- Bob



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

end of thread, other threads:[~2002-08-14  0:02 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-07-22 23:13 Dispatching and generics - language lawyer question Adam Beneschan
2002-07-23 15:42 ` Stephen Leake
2002-07-24 15:37   ` Stephen Leake
  -- strict thread matches above, loose matches on Subject: below --
2002-07-24  5:33 Grein, Christoph
2002-07-24 22:55 ` Robert A Duff
2002-07-25 15:46   ` Ben Brosgol
2002-07-29 20:38     ` Robert A Duff
2002-07-31 22:52       ` Dmitry A.Kazakov
2002-07-31 20:18         ` Robert A Duff
2002-08-02  1:15           ` Dmitry A.Kazakov
2002-08-01 16:30             ` Hyman Rosen
2002-08-02 23:42               ` Dmitry A.Kazakov
2002-08-02 15:49                 ` Hyman Rosen
2002-08-02 17:48                   ` Stephen Leake
2002-08-10  3:03                     ` Warren W. Gay VE3WWG
2002-08-05 11:15                   ` Dmitry A. Kazakov
2002-08-12 12:44                   ` Robert Dewar
2002-08-13 22:50           ` Randy Brukardt
2002-08-14  0:02             ` Robert A Duff
2002-07-25  0:40 ` Robert Dewar

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