comp.lang.ada
 help / color / mirror / Atom feed
* Instantiating private types with discriminants?
@ 2006-05-09 13:17 rick H
  2006-05-09 13:45 ` Georg Bauhaus
                   ` (3 more replies)
  0 siblings, 4 replies; 34+ messages in thread
From: rick H @ 2006-05-09 13:17 UTC (permalink / raw)



Hello,

I'm slowly learning Ada for my own amusement, and I've ground to a halt
trying to understand something.  If some kind sole could explain it to
me, I'd be very grateful.

I've defined two types, each with a descriminant, and each with its own
access type.  One of the type's implementations is, however, private:

package Discrim is
   type Type_A (Param : Integer := 100) is null record;
   type Type_A_Ptr is access Type_A;

   --  same as above, but implementation now private...
   type Type_B (Param : Integer := 100) is private;
   type Type_B_Ptr is access Type_B;
private
   type Type_B (Param : Integer := 100) is null record;
end Discrim;


When I use "new" on two variables declared as Type_A_Ptr and Type_B_Ptr,
one requires a type conversion for the discriminant, whereas the other
requires a qualified expression:

with Discrim; use Discrim;
procedure Use_Discrim is
   A : Type_A_Ptr;                   --  public  implementation
   B : Type_B_Ptr;                   --  private implementation
begin
   A := new Type_A'(Param => 100);   --  qualified expression
   B := new Type_B (Param => 123);   --  type conversion
end Use_Discrim;

So, my question to the experts is: Why does "privatising" a type's
details change the way that you "new" instantiations of it?

Thanks very much,
Rick



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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:17 Instantiating private types with discriminants? rick H
@ 2006-05-09 13:45 ` Georg Bauhaus
  2006-05-09 14:06   ` rick H
  2006-05-09 13:56 ` Ludovic Brenta
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 34+ messages in thread
From: Georg Bauhaus @ 2006-05-09 13:45 UTC (permalink / raw)


rick H wrote:


> So, my question to the experts is: Why does "privatising" a type's
> details change the way that you "new" instantiations of it?

You cannot write down the private components of a private type
anywhere they are invisible. Even though your private type Type_B
doesn't actually have private components, that isn't known outside
Discrim's private part, i.e. in procedure Use_Discrim . Consider:

with Discrim; use Discrim;
procedure Use_Discrim is
   A_Thing: Type_A;
   B_Thing: Type_B;
   Another_B_Thing: Type_B(Param => 42);  -- declaring with constraint
   A : Type_A_Ptr;                   --  public  implementation
   B : Type_B_Ptr;                   --  private implementation
begin
   A_Thing := (Param => 100);
   B_Thing := (Param => 100);  -- what about the private components?
   A_Thing := Type_A'(Param => 100);
   A_Thing := Type_A (Param => 100);
   A := new Type_A'(Param => 100);   --  qualified expression
   B := new Type_B (Param => 123);   --  ?
end Use_Discrim;




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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:17 Instantiating private types with discriminants? rick H
  2006-05-09 13:45 ` Georg Bauhaus
@ 2006-05-09 13:56 ` Ludovic Brenta
  2006-05-09 14:24   ` rick H
  2006-05-09 14:05 ` Dmitry A. Kazakov
  2006-05-09 14:48 ` rick H
  3 siblings, 1 reply; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-09 13:56 UTC (permalink / raw)



rick H a écrit :

> Hello,
>
> I'm slowly learning Ada for my own amusement, and I've ground to a halt
> trying to understand something.  If some kind sole could explain it to
> me, I'd be very grateful.
>
> I've defined two types, each with a descriminant, and each with its own
> access type.  One of the type's implementations is, however, private:
>
> package Discrim is
>    type Type_A (Param : Integer := 100) is null record;
>    type Type_A_Ptr is access Type_A;
>
>    --  same as above, but implementation now private...
>    type Type_B (Param : Integer := 100) is private;
>    type Type_B_Ptr is access Type_B;
> private
>    type Type_B (Param : Integer := 100) is null record;
> end Discrim;
>
>
> When I use "new" on two variables declared as Type_A_Ptr and Type_B_Ptr,
> one requires a type conversion for the discriminant, whereas the other
> requires a qualified expression:
>
> with Discrim; use Discrim;
> procedure Use_Discrim is
>    A : Type_A_Ptr;                   --  public  implementation
>    B : Type_B_Ptr;                   --  private implementation
> begin
>    A := new Type_A'(Param => 100);   --  qualified expression
>    B := new Type_B (Param => 123);   --  type conversion
> end Use_Discrim;
>
> So, my question to the experts is: Why does "privatising" a type's
> details change the way that you "new" instantiations of it?

Actually, the declaration of B is not a type conversion; it is an
allocator that uses a subtype_mark, as opposed to a
qualified_expression (see ARM 4.8(2)).

Use_Discrim sees all the components of Type_A (there is only one: the
discriminant), and so the qualified_expression is legal. But you could
also use an allocator with a subtype_mark, so it is incorrect to say
that A *requires* a qualified_expression; it only *allows* one:

A := new Type_A (Param => 100); -- legal

In contrast, Use_Discrim does not see the components of Type_B other
than the discriminant, so doesn't know if there are any, and so the
qualified_expression'aggregate (defined in 4.7(2)) would be illegal,
since aggregates must contain one expression for each component, per
4.3.1(9).

-- 
Ludovic Brenta.




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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:17 Instantiating private types with discriminants? rick H
  2006-05-09 13:45 ` Georg Bauhaus
  2006-05-09 13:56 ` Ludovic Brenta
@ 2006-05-09 14:05 ` Dmitry A. Kazakov
  2006-05-09 14:48 ` rick H
  3 siblings, 0 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-09 14:05 UTC (permalink / raw)


On Tue, 09 May 2006 13:17:36 GMT, rick H wrote:

> I'm slowly learning Ada for my own amusement, and I've ground to a halt
> trying to understand something.  If some kind sole could explain it to
> me, I'd be very grateful.
> 
> I've defined two types, each with a descriminant, and each with its own
> access type.  One of the type's implementations is, however, private:
> 
> package Discrim is
>    type Type_A (Param : Integer := 100) is null record;
>    type Type_A_Ptr is access Type_A;
> 
>    --  same as above, but implementation now private...
>    type Type_B (Param : Integer := 100) is private;
>    type Type_B_Ptr is access Type_B;
> private
>    type Type_B (Param : Integer := 100) is null record;
> end Discrim;
> 
> When I use "new" on two variables declared as Type_A_Ptr and Type_B_Ptr,
> one requires a type conversion for the discriminant, whereas the other
> requires a qualified expression:
> 
> with Discrim; use Discrim;
> procedure Use_Discrim is
>    A : Type_A_Ptr;                   --  public  implementation
>    B : Type_B_Ptr;                   --  private implementation
> begin
>    A := new Type_A'(Param => 100);   --  qualified expression
>    B := new Type_B (Param => 123);   --  type conversion
> end Use_Discrim;
> 
> So, my question to the experts is: Why does "privatising" a type's
> details change the way that you "new" instantiations of it?

They don't:

   A := new Type_A (Param => 100);   --  That's OK

The only difference is that you can't use aggregates with Type_B because
they are private. You don't know members. Note that you can still use
qualified expressions with Type_B if you have some other way of
constructing Type_B, without aggregates. So if Discrim had a function:

   function Get_Me_Some_B return Type_B;

then:

   B := new Type_B'(Get_Me_Some_B);  -- qualified expression

The only purpose of a qualified expression is to specify the expected type.

Further

   B := new Type_B (Param => 123);

is not a type conversion, it is constraining of Type_B. Type_B (Param =>
123) is a subtype specification. It is same as in:

   B_Object : Type_B (Param => 123); -- subtype of Type_B

You can also compare:

   A := new Type_A'(Param => 100);

with

   A_Object : Type_A := (Param => 100);

Type_A is the type of A_Object, (Param => 100) is an expression resulting
in the initial value. It could also well be:

   A_Object :  Type_A (Param => 100) := (Param => 100);

Here Type_A ((Param => 100) is the subtype of Type_A for all values of
A_Object. (Param => 100) appearing after ":=" is an expression in the form
of an aggregate. You can qualify it if you want:

   A_Object :  Type_A (Param => 100) := Type_A'(Param => 100);

You can do all this with Type_B, except for record aggregates, because they
are private.

Note that both:

   A_Object : Type_A;
   A := new Type_A;

are OK, because you have provided a default value for the discriminant.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:45 ` Georg Bauhaus
@ 2006-05-09 14:06   ` rick H
  0 siblings, 0 replies; 34+ messages in thread
From: rick H @ 2006-05-09 14:06 UTC (permalink / raw)


Georg Bauhaus <bauhaus@futureapps.de> wrote:
> rick H wrote:
> 
> 
>> So, my question to the experts is: Why does "privatising" a type's
>> details change the way that you "new" instantiations of it?
> 
> You cannot write down the private components of a private type
> anywhere they are invisible. Even though your private type Type_B
> doesn't actually have private components, that isn't known outside
> Discrim's private part, i.e. in procedure Use_Discrim . Consider:
> 
> with Discrim; use Discrim;
> procedure Use_Discrim is
>   A_Thing: Type_A;
>   B_Thing: Type_B;
>   Another_B_Thing: Type_B(Param => 42);  -- declaring with constraint
>   A : Type_A_Ptr;                   --  public  implementation
>   B : Type_B_Ptr;                   --  private implementation
> begin
>   A_Thing := (Param => 100);
>   B_Thing := (Param => 100);  -- what about the private components?
>   A_Thing := Type_A'(Param => 100);
>   A_Thing := Type_A (Param => 100);
>   A := new Type_A'(Param => 100);   --  qualified expression
>   B := new Type_B (Param => 123);   --  ?
> end Use_Discrim;
> 
Thanks Georg - I understand now!


Rick



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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:56 ` Ludovic Brenta
@ 2006-05-09 14:24   ` rick H
  2006-05-09 19:48     ` Ludovic Brenta
  0 siblings, 1 reply; 34+ messages in thread
From: rick H @ 2006-05-09 14:24 UTC (permalink / raw)


 Brenta <ludovic@ludovic-brenta.org> wrote:
>> with Discrim; use Discrim;
>> procedure Use_Discrim is
>>    A : Type_A_Ptr;                   --  public  implementation
>>    B : Type_B_Ptr;                   --  private implementation
>> begin
>>    A := new Type_A'(Param => 100);   --  qualified expression
>>    B := new Type_B (Param => 123);   --  type conversion
>> end Use_Discrim;
>>
>> So, my question to the experts is: Why does "privatising" a type's
>> details change the way that you "new" instantiations of it?
> 
> Actually, the declaration of B is not a type conversion; it is an
> allocator that uses a subtype_mark, as opposed to a
> qualified_expression (see ARM 4.8(2)).

Thanks, Ludovic - especially for the ARM references.  I now understand
that the expression for B was not a type conversion - though to a novice
it resembles one : a subtype_mark followed by something in brackets!

You say that B is an allocator with a subtype_mark - but ARM 3.2.2
suggests to me (probably wrong!) that it's a subtype_indication, because
it has a constraint (the discriminant).


--  
Rick



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

* Re: Instantiating private types with discriminants?
  2006-05-09 13:17 Instantiating private types with discriminants? rick H
                   ` (2 preceding siblings ...)
  2006-05-09 14:05 ` Dmitry A. Kazakov
@ 2006-05-09 14:48 ` rick H
  2006-05-09 15:20   ` Jerry Petrey
                     ` (3 more replies)
  3 siblings, 4 replies; 34+ messages in thread
From: rick H @ 2006-05-09 14:48 UTC (permalink / raw)


Thank you, Georg, Ludovic and Dmitri for your replies.

Now I know I won't get shouted at for asking novice questions, I have
another query for you experts, if you've got the time to have a look:

I'm trying to query the tag of a record, which I can do quite nicely,
provided I both "with Ada.Tags" and "use Ada.Tags".  Just for the sake
of it, I tried to remove the "use Ada.Tags" statement, but I can't work
out how I would then specify the XXX'Tag attribute of a variable.
Here's an example that illustrates my problem ("if Var_A'Tag..." is the
problem line):

with Ada.Integer_Text_IO;
with Ada.Float_Text_IO;
with Ada.Tags;
--  use Ada.Tags;

procedure Simple_Case is
   type General_T is tagged null record;

   type Access_T is access General_T'Class;


   type Type_A is new General_T with
   record
      Data : Integer;
   end record;


   type Type_B is new General_T with
   record
      Data : Float;
   end record;


   Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
begin
   --  Var_A := new Type_A' (Data => 100);
   Var_A := new Type_B' (Data => 100.0);

   --  problem line:
   if Var_A'Tag = Type_A'Tag then
      Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
   elsif Var_A'Tag = Type_B'Tag then
      Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
   end if;
end Simple_Case;



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

* Re: Instantiating private types with discriminants?
  2006-05-09 14:48 ` rick H
@ 2006-05-09 15:20   ` Jerry Petrey
  2006-05-09 15:42     ` rick H
  2006-05-09 15:53   ` Avoiding use Ada.Tags (was Re: Instantiating private types with discriminants?) Alex R. Mosteo
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 34+ messages in thread
From: Jerry Petrey @ 2006-05-09 15:20 UTC (permalink / raw)


rick H wrote:

> Thank you, Georg, Ludovic and Dmitri for your replies.
>
> Now I know I won't get shouted at for asking novice questions, I have
> another query for you experts, if you've got the time to have a look:
>
> I'm trying to query the tag of a record, which I can do quite nicely,
> provided I both "with Ada.Tags" and "use Ada.Tags".  Just for the sake
> of it, I tried to remove the "use Ada.Tags" statement, but I can't work
> out how I would then specify the XXX'Tag attribute of a variable.
> Here's an example that illustrates my problem ("if Var_A'Tag..." is the
> problem line):
>
> with Ada.Integer_Text_IO;
> with Ada.Float_Text_IO;
> with Ada.Tags;
> --  use Ada.Tags;
>
> procedure Simple_Case is
>    type General_T is tagged null record;
>
>    type Access_T is access General_T'Class;
>
>    type Type_A is new General_T with
>    record
>       Data : Integer;
>    end record;
>
>    type Type_B is new General_T with
>    record
>       Data : Float;
>    end record;
>
>    Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
> begin
>    --  Var_A := new Type_A' (Data => 100);
>    Var_A := new Type_B' (Data => 100.0);
>
>    --  problem line:
>    if Var_A'Tag = Type_A'Tag then
>       Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>    elsif Var_A'Tag = Type_B'Tag then
>       Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>    end if;
> end Simple_Case;

You could use function notation for the correct "=" as follows:

   if Ada.Tags."=" (Var_A'Tag, Type_A'Tag) then
      Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
   elsif Ada.Tags."=" (Var_A'Tag, Type_B'Tag) then
      Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
   end if;


Jerry

--
------------------------------------------------------------------------
-- Jerry Petrey, GNC Software Engineer, Raytheon Missile Systems
-- NOTE: please perform appendectomy on email address before replying
------------------------------------------------------------------------





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

* Re: Instantiating private types with discriminants?
  2006-05-09 15:20   ` Jerry Petrey
@ 2006-05-09 15:42     ` rick H
  0 siblings, 0 replies; 34+ messages in thread
From: rick H @ 2006-05-09 15:42 UTC (permalink / raw)


Jerry Petrey <jdpetreyAPPENDIX@raytheon.com> wrote:
> 
> You could use function notation for the correct "=" as follows:
> 
>   if Ada.Tags."=" (Var_A'Tag, Type_A'Tag) then
>      Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>   elsif Ada.Tags."=" (Var_A'Tag, Type_B'Tag) then
>      Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>   end if;
> 

Thanks, Jerry - that makes sense.
I recall ploughing (plowing) through John Barne's book a few weeks ago
where he referred to the function notation...and I had a quiet chuckle
when I realised that in Ada you can express
   C := A + B;
as
   C := Standard."+" (Left => A, Right => B);
if you really want to!


Rick




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

* Avoiding use Ada.Tags (was Re: Instantiating private types with discriminants?)
  2006-05-09 14:48 ` rick H
  2006-05-09 15:20   ` Jerry Petrey
@ 2006-05-09 15:53   ` Alex R. Mosteo
  2006-05-09 16:01   ` Instantiating private types with discriminants? Dmitry A. Kazakov
  2006-05-09 19:57   ` "Use" and "=" for Tags (was: Re: Instantiating private types with discriminants?) Jeffrey R. Carter
  3 siblings, 0 replies; 34+ messages in thread
From: Alex R. Mosteo @ 2006-05-09 15:53 UTC (permalink / raw)


rick H wrote:

> (...)
> I'm trying to query the tag of a record, which I can do quite nicely,
> provided I both "with Ada.Tags" and "use Ada.Tags".  Just for the sake
> of it, I tried to remove the "use Ada.Tags" statement, but I can't work
> out how I would then specify the XXX'Tag attribute of a variable.
> Here's an example that illustrates my problem ("if Var_A'Tag..." is the
> problem line):

>    --  problem line:
>    if Var_A'Tag = Type_A'Tag then
>       Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>    elsif Var_A'Tag = Type_B'Tag then
>       Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>    end if;
> end Simple_Case;

I offer you two alternatives. First one, would be:

with Ada.Tags;

procedure ...

   use type Ada.Tags.Tag;
   --  This makes operators on type Tag visible, without
   --  needing to use the whole package.
begin
   if Var_A'Tag = Type_A'Tag then -- Should work now.

And, the preferred method I guess, without "withing" Ada.Tags:

if Var_A.all in Type_A then -- Is A instance of Type_A?
if Var_A.all in Type_A'Class then -- Is Type_A or descendent?

See this test program:

----8<--------

with Text_Io; use Text_Io;

procedure Test_Tag is
   type X is tagged null record;
   type Y is new X with null record;
   type Z is new Y with null record;

   vz :          Z;
   vn : constant X'Class := vz;
begin
   Put_Line (Boolean'Image (vn in Y));
   Put_Line (Boolean'Image (vn in Y'Class));
end Test_Tag;

----8<--------



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

* Re: Instantiating private types with discriminants?
  2006-05-09 14:48 ` rick H
  2006-05-09 15:20   ` Jerry Petrey
  2006-05-09 15:53   ` Avoiding use Ada.Tags (was Re: Instantiating private types with discriminants?) Alex R. Mosteo
@ 2006-05-09 16:01   ` Dmitry A. Kazakov
  2006-05-10  7:42     ` rick H
  2006-05-09 19:57   ` "Use" and "=" for Tags (was: Re: Instantiating private types with discriminants?) Jeffrey R. Carter
  3 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-09 16:01 UTC (permalink / raw)


On Tue, 09 May 2006 14:48:44 GMT, rick H wrote:

> Thank you, Georg, Ludovic and Dmitri for your replies.
> 
> Now I know I won't get shouted at for asking novice questions, I have
> another query for you experts, if you've got the time to have a look:
> 
> I'm trying to query the tag of a record, which I can do quite nicely,
> provided I both "with Ada.Tags" and "use Ada.Tags".  Just for the sake
> of it, I tried to remove the "use Ada.Tags" statement, but I can't work
> out how I would then specify the XXX'Tag attribute of a variable.
> Here's an example that illustrates my problem ("if Var_A'Tag..." is the
> problem line):
> 
> with Ada.Integer_Text_IO;
> with Ada.Float_Text_IO;
> with Ada.Tags;
> --  use Ada.Tags;
> 
> procedure Simple_Case is
>    type General_T is tagged null record;
> 
>    type Access_T is access General_T'Class;
> 
> 
>    type Type_A is new General_T with
>    record
>       Data : Integer;
>    end record;
> 
> 
>    type Type_B is new General_T with
>    record
>       Data : Float;
>    end record;
> 
> 
>    Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
> begin
>    --  Var_A := new Type_A' (Data => 100);
>    Var_A := new Type_B' (Data => 100.0);
> 
>    --  problem line:
>    if Var_A'Tag = Type_A'Tag then
>       Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>    elsif Var_A'Tag = Type_B'Tag then
>       Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>    end if;
> end Simple_Case;

You can do:

   if Var_A.all in Type_A then

which is cleaner, and requires no reference to tags.

BTW, You should probably make Put a primitive operation of General_T. Then
the whole could look like simply:

   Put (Var_A.all); -- Do what have to be done

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-09 14:24   ` rick H
@ 2006-05-09 19:48     ` Ludovic Brenta
  0 siblings, 0 replies; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-09 19:48 UTC (permalink / raw)


rick H <rik_nntp@dsl.pipex.com> writes:
> You say that B is an allocator with a subtype_mark - but ARM 3.2.2
> suggests to me (probably wrong!) that it's a subtype_indication,
> because it has a constraint (the discriminant).

Yes, you are correct.  Thanks for rectifying me, and congratulations
on spotting this mistake so quickly!

-- 
Ludovic Brenta.




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

* "Use" and "=" for Tags (was: Re: Instantiating private types with discriminants?)
  2006-05-09 14:48 ` rick H
                     ` (2 preceding siblings ...)
  2006-05-09 16:01   ` Instantiating private types with discriminants? Dmitry A. Kazakov
@ 2006-05-09 19:57   ` Jeffrey R. Carter
  3 siblings, 0 replies; 34+ messages in thread
From: Jeffrey R. Carter @ 2006-05-09 19:57 UTC (permalink / raw)


> 
>    --  problem line:
>    if Var_A'Tag = Type_A'Tag then
>       Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>    elsif Var_A'Tag = Type_B'Tag then
>       Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>    end if;

You've received some specific solutions, but no general discussion of 
why this doesn't work. The function "=" you're trying to call is defined 
in Ada.Tags, and not directly visible here. You can make it directly 
visible with a "use" clause for Ada.Tags, as you've already discovered, 
or with a "use type" clause for Ada.Tags.Tag. You can use prefix 
notation to access it [Ada.Tags."=" (...)]. The subtype membership 
operation {"in"} is not a subprogram and is always visible, like 
assignment (":="). Finally, you have the Ada-83 solution to avoiding 
"use", renaming:

function "=" (Left : Ada.Tags.Tag; Right : Ada.Tags.Tag) return Boolean
renames Ada.Tags."=";
...
if This_Tag = That_Tag then

The operators for the predefined types, being in Standard, are always 
directly visible, so you don't encounter this problem with them. For the 
most part, though, well designed Ada deals with user-defined types, so 
understanding this concept is useful.

-- 
Jeff Carter
"Apart from the sanitation, the medicine, education, wine,
public order, irrigation, roads, the fresh water system,
and public health, what have the Romans ever done for us?"
Monty Python's Life of Brian
80



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

* Re: Instantiating private types with discriminants?
  2006-05-09 16:01   ` Instantiating private types with discriminants? Dmitry A. Kazakov
@ 2006-05-10  7:42     ` rick H
  2006-05-10  9:09       ` Ludovic Brenta
  0 siblings, 1 reply; 34+ messages in thread
From: rick H @ 2006-05-10  7:42 UTC (permalink / raw)


Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
> On Tue, 09 May 2006 14:48:44 GMT, rick H wrote:
>>    Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
>> begin
>>    --  Var_A := new Type_A' (Data => 100);
>>    Var_A := new Type_B' (Data => 100.0);
>> 
>>    --  problem line:
>>    if Var_A'Tag = Type_A'Tag then
>>       Ada.Integer_Text_IO.Put ( Type_A (Var_A.all).Data);
>>    elsif Var_A'Tag = Type_B'Tag then
>>       Ada.Float_Text_IO.Put ( Type_B (Var_A.all).Data);
>>    end if;
>> end Simple_Case;
> 
> You can do:
> 
>   if Var_A.all in Type_A then
> 
> which is cleaner, and requires no reference to tags.

Thanks to everyone for their help regarding the tags - I understand that
now.

> 
> BTW, You should probably make Put a primitive operation of General_T. Then
> the whole could look like simply:
> 
>   Put (Var_A.all); -- Do what have to be done
> 


I'm still a bit new to Ada terminology - does "making Put a primitive
operation" mean doing this kind of thing with a generic package...
   type Fred is new Integer;
   package Fred_IO is new Ada.Text_IO.Integer_IO (Fred);
   use Fred_IO;
If that's right - how would I do this in my example? I'm puzzled
because Var_A.all.Data will resolve to either an Integer or a Float at
run-time.  Have I got to declare two new packages - one against
Integer_IO and one for Float_IO and let the dispatcher get it right
at run-time?

--  
Rick




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

* Re: Instantiating private types with discriminants?
  2006-05-10  7:42     ` rick H
@ 2006-05-10  9:09       ` Ludovic Brenta
  2006-05-10 11:49         ` Georg Bauhaus
  2006-05-10 13:44         ` rick H
  0 siblings, 2 replies; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-10  9:09 UTC (permalink / raw)


rick H writes:
> Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
> > BTW, You should probably make Put a primitive operation of General_T. Then
> > the whole could look like simply:
> >
> >   Put (Var_A.all); -- Do what have to be done
>
> I'm still a bit new to Ada terminology - does "making Put a primitive
> operation" mean doing this kind of thing with a generic package...
>    type Fred is new Integer;
>    package Fred_IO is new Ada.Text_IO.Integer_IO (Fred);
>    use Fred_IO;
> If that's right - how would I do this in my example? I'm puzzled
> because Var_A.all.Data will resolve to either an Integer or a Float at
> run-time.  Have I got to declare two new packages - one against
> Integer_IO and one for Float_IO and let the dispatcher get it right
> at run-time?

A "primitive operation" of a type T is a subprogram that:

- takes a parameter of type T or "access T", or returns a result of
type T or "access T"

- is declared in the same package specification as T (same declarative
region, really)

If these conditions are met, then the operation is primitive.  If, in
addition, T is tagged, then a call to a primitive operation dispatches
dynamically at run time according to the exact type T. Essentially, the
compiler does the tag comparison and dispatching for you. So you could
write:

package P is
   type T is tagged null record;
   procedure Put (Item : in T); -- primitive operation

   type Type_A is new T with Data : Integer; end record;
   procedure Put (Item : in Type_A); -- primitive operation

   type Type_B is new T with Data : Float; end record;
   procedure Put (Item : in Type_B); -- also primitive
end P;

package body P is
   procedure Put (Item : in T) is
   begin
       null;
   end Put; -- we could also have declared this procedure abstract

procedure Put (Item : in Type_A) is
begin
   Ada.Integer_Text_IO.Put (Item.Data);
end Put;

procedure Put (Item : in Type_B) is
begin
   Ada.Float_Text_IO.Put (Item.Data);
end Put;
end P;

with P;
procedure Simple_Case is
   Var_A : P.T'Class := ...; --  class-wide type: could be T, Type_A or
Type_B
begin
   P.Put (Var_A); -- dynamic dispatch to suitable version of Put
end Simple_Case;

You can compare Ada's primitive operations with "virtual member
functions" in C++.  There are however two differences:

- in C++, you need pointers or references to dispatch dynamically. In
Ada, you can also dispatch with plain variables, as illustrated above.
- in C++, all calls done through a pointer or reference dispatch
dynamically. In Ada, the call dispatches if and only if the actual
parameter is of a class-wide type, i.e. is declared like in my example:
Var_A : P.T'Class.  In other words, in C++, all types are implicitly
class-wide, not in Ada.

with P;
procedure Simple_Case is
   Var_A : P.T'Class := ...; --  class-wide type
   Var_B : P.Type_B; -- specific type
   Var_T : P.T; -- specific type, contrast with Var_A
begin
   P.Put (P.Type_A (Var_A)); -- explicit type conversion with check,
then *static* dispatch
   P.Put (Var_B); -- static dispatch
   P.Put (Var_T); -- static dispatch to the null procedure
end Simple_Case;

-- 
Ludovic Brenta.




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

* Re: Instantiating private types with discriminants?
  2006-05-10  9:09       ` Ludovic Brenta
@ 2006-05-10 11:49         ` Georg Bauhaus
  2006-05-10 13:44         ` rick H
  1 sibling, 0 replies; 34+ messages in thread
From: Georg Bauhaus @ 2006-05-10 11:49 UTC (permalink / raw)


Ludovic Brenta wrote:

>  If, in
> addition, T is tagged, then a call to a primitive operation dispatches
> dynamically at run time according to the exact type T. 

Or statically, as you describe a little later, if there is
no doubt about the type of a specific tagged object.



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

* Re: Instantiating private types with discriminants?
  2006-05-10  9:09       ` Ludovic Brenta
  2006-05-10 11:49         ` Georg Bauhaus
@ 2006-05-10 13:44         ` rick H
  2006-05-10 14:21           ` Ludovic Brenta
  2006-05-10 14:41           ` Dmitry A. Kazakov
  1 sibling, 2 replies; 34+ messages in thread
From: rick H @ 2006-05-10 13:44 UTC (permalink / raw)


Ludovic Brenta <ludovic@ludovic-brenta.org> wrote:
> rick H writes:
>> Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
>> > BTW, You should probably make Put a primitive operation of General_T. Then
>> > the whole could look like simply:
>> >
>> >   Put (Var_A.all); -- Do what have to be done
>>
>> I'm still a bit new to Ada terminology - does "making Put a primitive
>> operation" mean doing this kind of thing with a generic package...
>>    type Fred is new Integer;
>>    package Fred_IO is new Ada.Text_IO.Integer_IO (Fred);
>>    use Fred_IO;
>> If that's right - how would I do this in my example? I'm puzzled
>> because Var_A.all.Data will resolve to either an Integer or a Float at
>> run-time.  Have I got to declare two new packages - one against
>> Integer_IO and one for Float_IO and let the dispatcher get it right
>> at run-time?
> 
> A "primitive operation" of a type T is a subprogram that:
> 
> - takes a parameter of type T or "access T", or returns a result of
> type T or "access T"
> 
> - is declared in the same package specification as T (same declarative
> region, really)
> 
> If these conditions are met, then the operation is primitive.  If, in
> addition, T is tagged, then a call to a primitive operation dispatches
> dynamically at run time according to the exact type T. Essentially, the
> compiler does the tag comparison and dispatching for you. So you could
> write:
> 
> package P is
>   type T is tagged null record;
>   procedure Put (Item : in T); -- primitive operation
> 
>   type Type_A is new T with Data : Integer; end record;
>   procedure Put (Item : in Type_A); -- primitive operation
> 
>   type Type_B is new T with Data : Float; end record;
>   procedure Put (Item : in Type_B); -- also primitive
> end P;
> 
> package body P is
>   procedure Put (Item : in T) is
>   begin
>       null;
>   end Put; -- we could also have declared this procedure abstract
> 
> procedure Put (Item : in Type_A) is
> begin
>   Ada.Integer_Text_IO.Put (Item.Data);
> end Put;
> 
> procedure Put (Item : in Type_B) is
> begin
>   Ada.Float_Text_IO.Put (Item.Data);
> end Put;
> end P;
> 
> with P;
> procedure Simple_Case is
>   Var_A : P.T'Class := ...; --  class-wide type: could be T, Type_A or
> Type_B
> begin
>   P.Put (Var_A); -- dynamic dispatch to suitable version of Put
> end Simple_Case;
> 

Thanks, Ludovic.  Bearing in mind that, in my original example, Var_A is
declared as an *access* to the class-wide type, I tried declaring the
various Put procedures as you showed, and then simply dereferencing
Var_A when calling Put:
   Put (Var_A.all);
However, the compiler (gnat) complained that I cannot use a class-wide
argument in this case.
What I had to do is to create two new types - one to access Type_A and
one to access Type_B.  Then I can call Put without dereferencing Var_A,
but at the expense of having to perform a type-conversion, e.g.
   Put (Type_B_Ptr(Var_A));

Obviously this gets me back to where I was before - having to check the
type prior to converting that type to either Type_A_Ptr or to
Type_B_Ptr...

if Var_A.all in Type_A then
   Put (Type_A_Ptr (Var_A));
elsif Var_A.all in Type_B then
   Put (Type_A_Ptr (Var_A));
end if;



Rick



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

* Re: Instantiating private types with discriminants?
  2006-05-10 13:44         ` rick H
@ 2006-05-10 14:21           ` Ludovic Brenta
  2006-05-10 15:10             ` rick H
  2006-05-10 14:41           ` Dmitry A. Kazakov
  1 sibling, 1 reply; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-10 14:21 UTC (permalink / raw)


rick H wrote :
> Thanks, Ludovic.  Bearing in mind that, in my original example, Var_A is
> declared as an *access* to the class-wide type, I tried declaring the
> various Put procedures as you showed, and then simply dereferencing
> Var_A when calling Put:
>    Put (Var_A.all);
> However, the compiler (gnat) complained that I cannot use a class-wide
> argument in this case.

This sounds suspicious; it should work. Could you post your full source
code? I think there may be a small mistake somewhere.

-- 
Ludovic Brenta.




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

* Re: Instantiating private types with discriminants?
  2006-05-10 13:44         ` rick H
  2006-05-10 14:21           ` Ludovic Brenta
@ 2006-05-10 14:41           ` Dmitry A. Kazakov
  2006-05-10 15:34             ` rick H
  1 sibling, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-10 14:41 UTC (permalink / raw)


On Wed, 10 May 2006 13:44:00 GMT, rick H wrote:

> Ludovic Brenta <ludovic@ludovic-brenta.org> wrote:
>> rick H writes:
>>> Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
>>> > BTW, You should probably make Put a primitive operation of General_T. Then
>>> > the whole could look like simply:
>>> >
>>> >   Put (Var_A.all); -- Do what have to be done
>>>
>>> I'm still a bit new to Ada terminology - does "making Put a primitive
>>> operation" mean doing this kind of thing with a generic package...
>>>    type Fred is new Integer;
>>>    package Fred_IO is new Ada.Text_IO.Integer_IO (Fred);
>>>    use Fred_IO;
>>> If that's right - how would I do this in my example? I'm puzzled
>>> because Var_A.all.Data will resolve to either an Integer or a Float at
>>> run-time.  Have I got to declare two new packages - one against
>>> Integer_IO and one for Float_IO and let the dispatcher get it right
>>> at run-time?
>> 
>> A "primitive operation" of a type T is a subprogram that:
>> 
>> - takes a parameter of type T or "access T", or returns a result of
>> type T or "access T"
>> 
>> - is declared in the same package specification as T (same declarative
>> region, really)
>> 
>> If these conditions are met, then the operation is primitive.  If, in
>> addition, T is tagged, then a call to a primitive operation dispatches
>> dynamically at run time according to the exact type T. Essentially, the
>> compiler does the tag comparison and dispatching for you. So you could
>> write:
>> 
>> package P is
>>   type T is tagged null record;
>>   procedure Put (Item : in T); -- primitive operation
>> 
>>   type Type_A is new T with Data : Integer; end record;
>>   procedure Put (Item : in Type_A); -- primitive operation
>> 
>>   type Type_B is new T with Data : Float; end record;
>>   procedure Put (Item : in Type_B); -- also primitive
>> end P;
>> 
>> package body P is
>>   procedure Put (Item : in T) is
>>   begin
>>       null;
>>   end Put; -- we could also have declared this procedure abstract
>> 
>> procedure Put (Item : in Type_A) is
>> begin
>>   Ada.Integer_Text_IO.Put (Item.Data);
>> end Put;
>> 
>> procedure Put (Item : in Type_B) is
>> begin
>>   Ada.Float_Text_IO.Put (Item.Data);
>> end Put;
>> end P;
>> 
>> with P;
>> procedure Simple_Case is
>>   Var_A : P.T'Class := ...; --  class-wide type: could be T, Type_A or
>> Type_B
>> begin
>>   P.Put (Var_A); -- dynamic dispatch to suitable version of Put
>> end Simple_Case;
>> 
> Thanks, Ludovic.  Bearing in mind that, in my original example, Var_A is
> declared as an *access* to the class-wide type, I tried declaring the
> various Put procedures as you showed, and then simply dereferencing
> Var_A when calling Put:
>    Put (Var_A.all);
> However, the compiler (gnat) complained that I cannot use a class-wide
> argument in this case.

You should have tried it in the same body. Do it as Ludovic did, using
packages:

---------- Simple_Case_Types.ads
package Simple_Case_Types is
   type General_T is abstract tagged null record;
   procedure Put (Item : in General_T) is abstract;
   
   type Type_A is new General_T with
   record
      Data : Integer;
   end record;
   procedure Put (Item : in Type_A);

   type Type_B is new General_T with
   record
      Data : Float;
   end record;
   procedure Put (Item : in Type_B);

end Simple_Case_Types;

---------- Simple_Case_Types.adb
with Ada.Integer_Text_IO;
with Ada.Float_Text_IO;

package body Simple_Case_Types is
   procedure Put (Item : in Type_A) is
   begin
      Ada.Integer_Text_IO.Put (Item.Data);
   end Put;

   procedure Put (Item : in Type_B) is
   begin
      Ada.Float_Text_IO.Put (Item.Data);
   end Put;

end Simple_Case_Types;

----------- Simple_Case.adb
with Simple_Case_Types;  use Simple_Case_Types;

procedure Simple_Case is
   type Access_T is access General_T'Class;
   Var_A : Access_T; -- could be "new Type_A" or a "new Type_B"
begin
   Var_A := new Type_B' (Data => 100.0);
   Put (Var_A.all);

end Simple_Case;

This will compile as expected.

BTW, I should use pointers only when needed. Differently to C++, in Ada you
can create class-wide objects on the stack.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-10 14:21           ` Ludovic Brenta
@ 2006-05-10 15:10             ` rick H
  2006-05-10 15:45               ` Ludovic Brenta
  0 siblings, 1 reply; 34+ messages in thread
From: rick H @ 2006-05-10 15:10 UTC (permalink / raw)


Ludovic Brenta <ludovic@ludovic-brenta.org> wrote:
> rick H wrote :
>> Thanks, Ludovic.  Bearing in mind that, in my original example, Var_A is
>> declared as an *access* to the class-wide type, I tried declaring the
>> various Put procedures as you showed, and then simply dereferencing
>> Var_A when calling Put:
>>    Put (Var_A.all);
>> However, the compiler (gnat) complained that I cannot use a class-wide
>> argument in this case.
> 
> This sounds suspicious; it should work. Could you post your full source
> code? I think there may be a small mistake somewhere.
>

with Ada.Integer_Text_IO;
with Ada.Float_Text_IO;

procedure Simple_Case is

   type General_T is tagged null record;
   type Access_T is access General_T'Class;

   type Type_A is new General_T with
   record
      Data : Integer;
   end record;

   type Type_B is new General_T with
   record
      Data : Float;
   end record;

   procedure Put (Item: in General_T) is
   begin
      null;
   end Put;

   procedure Put (Item: in Type_A) is
   begin
      Ada.Integer_Text_IO.Put (Item.Data);
   end Put;

   procedure Put (Item: in Type_B) is
   begin
      Ada.Float_Text_IO.Put (Item.Data);
   end Put;

   Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
begin

   --  Var_A := new Type_A' (Data => 100);
   Var_A := new Type_B' (Data => 100.0);

   Put (Var_A.all);


end Simple_Case;
 



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

* Re: Instantiating private types with discriminants?
  2006-05-10 14:41           ` Dmitry A. Kazakov
@ 2006-05-10 15:34             ` rick H
  2006-05-10 19:01               ` Georg Bauhaus
  0 siblings, 1 reply; 34+ messages in thread
From: rick H @ 2006-05-10 15:34 UTC (permalink / raw)


Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
> You should have tried it in the same body. Do it as Ludovic did, using
> packages:
> 
> ---------- Simple_Case_Types.ads
> package Simple_Case_Types is
>   type General_T is abstract tagged null record;
>   procedure Put (Item : in General_T) is abstract;
>   
>   type Type_A is new General_T with
>   record
>      Data : Integer;
>   end record;
>   procedure Put (Item : in Type_A);
> 
>   type Type_B is new General_T with
>   record
>      Data : Float;
>   end record;
>   procedure Put (Item : in Type_B);
> 
> end Simple_Case_Types;
> 
> ---------- Simple_Case_Types.adb
> with Ada.Integer_Text_IO;
> with Ada.Float_Text_IO;
> 
> package body Simple_Case_Types is
>   procedure Put (Item : in Type_A) is
>   begin
>      Ada.Integer_Text_IO.Put (Item.Data);
>   end Put;
> 
>   procedure Put (Item : in Type_B) is
>   begin
>      Ada.Float_Text_IO.Put (Item.Data);
>   end Put;
> 
> end Simple_Case_Types;
> 
> ----------- Simple_Case.adb
> with Simple_Case_Types;  use Simple_Case_Types;
> 
> procedure Simple_Case is
>   type Access_T is access General_T'Class;
>   Var_A : Access_T; -- could be "new Type_A" or a "new Type_B"
> begin
>   Var_A := new Type_B' (Data => 100.0);
>   Put (Var_A.all);
> 
> end Simple_Case;
> 
> This will compile as expected.
> 
> BTW, I should use pointers only when needed. Differently to C++, in Ada you
> can create class-wide objects on the stack.
> 

I'm quite happy to abandon pointers in preference to the class-wide
object approach - in all honestly I didn't know that you could declare
variables of a class-wide type - and this is a side-effect of a novice
not having "Programming in Ada" open when coding!

I've now tried declaring Var_A to be...
   Var_A : General_T'Class;

but gnat insists on initialization, and it's here that I tumble.

I can declare Var_A with an initialization...
   Var_A : Type_B' (Data => 100.0);

and then the right Put will work...
   Put (Var_A);

However, I can't work out how to change Var_A dynamically - the compiler
says "dynamically tagged expression required" if I do...
begin
   Var_A := Type_A' (Data => 100);
   Put (Var_A);
end;




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

* Re: Instantiating private types with discriminants?
  2006-05-10 15:10             ` rick H
@ 2006-05-10 15:45               ` Ludovic Brenta
  0 siblings, 0 replies; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-10 15:45 UTC (permalink / raw)


rick H a écrit :
> with Ada.Integer_Text_IO;
> with Ada.Float_Text_IO;
>
> procedure Simple_Case is
>
>    type General_T is tagged null record;
>    type Access_T is access General_T'Class;
>
>    type Type_A is new General_T with
>    record
>       Data : Integer;
>    end record;
>
>    type Type_B is new General_T with
>    record
>       Data : Float;
>    end record;
>
>    procedure Put (Item: in General_T) is
>    begin
>       null;
>    end Put;
>
>    procedure Put (Item: in Type_A) is
>    begin
>       Ada.Integer_Text_IO.Put (Item.Data);
>    end Put;
>
>    procedure Put (Item: in Type_B) is
>    begin
>       Ada.Float_Text_IO.Put (Item.Data);
>    end Put;
>
>    Var_A : Access_T; --  could be "new Type_A" or a "new Type_B"
> begin
>
>    --  Var_A := new Type_A' (Data => 100);
>    Var_A := new Type_B' (Data => 100.0);
>
>    Put (Var_A.all);
> end Simple_Case;

First , per ARM 3.2.3, you should declare the three types and the Put
procedures inside a package (possibly nested in the Simple_Case
procedure), so that they fall under ARM 3.2.3(6). Otherwise, the Put
procedures are not primitive, and therefore not dispatching.

Secondly, you are being hit by what may be Ada's most subtle rules:
freezing rules (ARM 13.14).

The declaration of type Type_A freezes General_T, per ARM 13.14(7).
This means that after the declaration of Type_A, you can no longer
declare primitive subprograms of General_T; any subprograms you declare
thereafter are non-primitive, and so dynamic dispatching is forbidden.
This is an opportunity to praise GNAT's excellent warnings:

dispatch.adb:11:07: warning: no primitive operations for "General_T"
after this line
(this is the line declaring Type_A)

dispatch.adb:19:17: this primitive operation is declared too late
(this is the line declaring Put (General_T)

dispatch.adb:52:15: class-wide argument not allowed here
dispatch.adb:52:15: "Put" is not a primitive operation of "General_T"
(quite explicit when you know the language rules)

Move the declaration of Put (General_Type) before the declaration of
Type_A, and the declaration of Put (Type_A) before the declaration of
Type_B (which freezes Type_A). This will make these subprograms
primitive.

The basic idea behind freezing rules is that you cannot add a primitive
operation to the vtable of a tagged type after declaring another type
derived from it.

-- 
Ludovic Brenta.




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

* Re: Instantiating private types with discriminants?
  2006-05-10 15:34             ` rick H
@ 2006-05-10 19:01               ` Georg Bauhaus
  2006-05-10 19:05                 ` Ludovic Brenta
  0 siblings, 1 reply; 34+ messages in thread
From: Georg Bauhaus @ 2006-05-10 19:01 UTC (permalink / raw)


rick H wrote:

> I've now tried declaring Var_A to be...
>    Var_A : General_T'Class;
> 
> but gnat insists on initialization, and it's here that I tumble.

A real object of a class-wide type needs to have a tag,
hence if you don't initialize, which tag should it be?
Solution:
Declare variables or constants where you need them,
and where you have assembled everything necessary for their
initialization. That is,

procedure simple_case is
   ...
begin
   ...
   declare
      var_x: General_T'class := <your initialization here>;
   begin
      op(var_x);
   end;
   ...
end simple_case;



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

* Re: Instantiating private types with discriminants?
  2006-05-10 19:01               ` Georg Bauhaus
@ 2006-05-10 19:05                 ` Ludovic Brenta
  2006-05-10 21:52                   ` Rick H
  0 siblings, 1 reply; 34+ messages in thread
From: Ludovic Brenta @ 2006-05-10 19:05 UTC (permalink / raw)


Here is a complete, working example, in case you're confused.  It
summarises everything we've said before: package (possibly, like here,
nested in the procedure), freezing rules, abstract procedure in the
base type, no access type, and declaring the object at the last
moment.

Now, can you answer this question: which subprograms in that example
are primitive and which ones are not?

-- 
Ludovic Brenta.

with Ada.Float_Text_IO;
with Ada.Integer_Text_IO;
with Ada.Text_IO;
procedure Dispatch is
   package P is
      type General_T is abstract tagged null record;

      function Get return General_T'Class;
      procedure Put (Item : in General_T) is abstract;

      type Type_A is new General_T with record
         Data : Integer;
      end record;

      procedure Put (Item : in Type_A);

      type Type_B is new General_T with record
         Data : Float;
      end record;

      procedure Put (Item : in Type_B);
   end P;


   package body P is
      function Get return General_T'Class is
      begin
         return Type_A'(Data => 10);
      end Get;

      procedure Put (Item : in Type_A) is
      begin
         Ada.Text_IO.Put ("A: ");
         Ada.Integer_Text_IO.Put (Item.Data);
      end Put;

      procedure Put (Item : in Type_B) is
      begin
         Ada.Text_IO.Put ("B: ");
         Ada.Float_Text_IO.Put (Item.Data);
      end Put;
   end P;

   use P;
   Var_A : General_T'Class := Get;
begin
   Put (Var_A);
end Dispatch;



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

* Re: Instantiating private types with discriminants?
  2006-05-10 19:05                 ` Ludovic Brenta
@ 2006-05-10 21:52                   ` Rick H
  2006-05-11  1:17                     ` Jeffrey R. Carter
  2006-05-11  7:44                     ` Dmitry A. Kazakov
  0 siblings, 2 replies; 34+ messages in thread
From: Rick H @ 2006-05-10 21:52 UTC (permalink / raw)


Ludovic Brenta <ludovic@ludovic-brenta.org> wrote:
> Here is a complete, working example, in case you're confused.  It
> summarises everything we've said before: package (possibly, like here,
> nested in the procedure), freezing rules, abstract procedure in the
> base type, no access type, and declaring the object at the last
> moment.
> 
> Now, can you answer this question: which subprograms in that example
> are primitive and which ones are not?
> 
Thanks, Ludovic.  I extented your example by parameterising Get
and creating two versions:

function Get (Item : Integer) return General_T'Class is
begin
   return Type_A' (Data => Item);
end Get;

function Get (Item : Float) return General_T'Class is
begin
   return Type_B' (Data => Item);
end Get;

and then Var_A can be declared as either of:
Var_A : General_T'Class := Get (10);
or
Var_A : General_T'Class := Get (10.0);


But all this has a side-effect that I haven't considered: the fact
that a variable declared as a class-wide type *requires* initialization
means that the compiler can determine the variable's type statically.
This means that the following will raise a constraint error:
declare
   Var_A : General_T'Class := Get (10);  -- Type_A
begin
   Put (Var_A);
   Ada.Text_IO.New_Line;
   Var_A := Get (100.0);      -- Type_B - constraint error!
   Put (Var_A);
end;



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

* Re: Instantiating private types with discriminants?
  2006-05-10 21:52                   ` Rick H
@ 2006-05-11  1:17                     ` Jeffrey R. Carter
  2006-05-11  7:44                     ` Dmitry A. Kazakov
  1 sibling, 0 replies; 34+ messages in thread
From: Jeffrey R. Carter @ 2006-05-11  1:17 UTC (permalink / raw)


Rick H wrote:
> 
> But all this has a side-effect that I haven't considered: the fact
> that a variable declared as a class-wide type *requires* initialization
> means that the compiler can determine the variable's type statically.
> This means that the following will raise a constraint error:
> declare
>    Var_A : General_T'Class := Get (10);  -- Type_A
> begin
>    Put (Var_A);
>    Ada.Text_IO.New_Line;
>    Var_A := Get (100.0);      -- Type_B - constraint error!
>    Put (Var_A);
> end;

Constraint_Error is raised at run time, so this isn't static. In the 
most general case, a function returning the class-wide type may return 
any of the types in the class based on external events, and so be 
completely unknown statically (no matter how good a job of static 
analysis the compiler does). So there must be cases when the actual type 
of the variable is not known until run time. In practice, most compilers 
are not going to bother, so this will always result in dispatching.

One thing about class-wide variables is that they are constrained to the 
type of their initialization expression, and so can never be assigned 
another type in the class (hence Constraint_Error). This is why it's 
best to declare them as late as possible.

-- 
Jeff Carter
"My name is Jim, but most people call me ... Jim."
Blazing Saddles
39



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

* Re: Instantiating private types with discriminants?
  2006-05-10 21:52                   ` Rick H
  2006-05-11  1:17                     ` Jeffrey R. Carter
@ 2006-05-11  7:44                     ` Dmitry A. Kazakov
  2006-05-11  8:27                       ` rick H
  1 sibling, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-11  7:44 UTC (permalink / raw)


On Wed, 10 May 2006 16:52:32 -0500, Rick H wrote:

> But all this has a side-effect that I haven't considered: the fact
> that a variable declared as a class-wide type *requires* initialization
> means that the compiler can determine the variable's type statically.

No. It only means that the type should be determinable at run-time. There
are may ways to do it. You can call a function with a class-wide result.

It might be especially interesting to take a look at S'Class'Input
attribute. It "dispatches" on a "bare" tag. [ Ada 2005 will offer a
mechanism for fully user-defined things like this. See
Generic_Dispatching_Constructor. ]

> This means that the following will raise a constraint error:
> declare
>    Var_A : General_T'Class := Get (10);  -- Type_A
> begin
>    Put (Var_A);
>    Ada.Text_IO.New_Line;
>    Var_A := Get (100.0);      -- Type_B - constraint error!
>    Put (Var_A);
> end;

Yes, but this is a different thing. You cannot change tag, either
dynamically or statically determined. Type mutators are no-no in Ada's type
system.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-11  7:44                     ` Dmitry A. Kazakov
@ 2006-05-11  8:27                       ` rick H
  2006-05-11 10:28                         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: rick H @ 2006-05-11  8:27 UTC (permalink / raw)


Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
> On Wed, 10 May 2006 16:52:32 -0500, Rick H wrote:
> 
>> But all this has a side-effect that I haven't considered: the fact
>> that a variable declared as a class-wide type *requires* initialization
>> means that the compiler can determine the variable's type statically.
> 
> No. It only means that the type should be determinable at run-time. There
> are may ways to do it. You can call a function with a class-wide result.
> 
> It might be especially interesting to take a look at S'Class'Input
> attribute. It "dispatches" on a "bare" tag. [ Ada 2005 will offer a
> mechanism for fully user-defined things like this. See
> Generic_Dispatching_Constructor. ]
> 
>> This means that the following will raise a constraint error:
>> declare
>>    Var_A : General_T'Class := Get (10);  -- Type_A
>> begin
>>    Put (Var_A);
>>    Ada.Text_IO.New_Line;
>>    Var_A := Get (100.0);      -- Type_B - constraint error!
>>    Put (Var_A);
>> end;
> 
> Yes, but this is a different thing. You cannot change tag, either
> dynamically or statically determined. Type mutators are no-no in Ada's type
> system.
> 
Thanks Dmitry & Jeffrey.
Objects of class-wide types notwithstanding, I modified Ludovic's code
example to change Type_A and Type_B into linked-list elements.  That
meant no more objects of class-wide type, so I got rid of the Get
definitions.  The following code does exactly what I wanted from this
cut-down example: a list of either-Integer-or-Floats.  Next mystery:
what's the use of abstract subprograms? Put (X:in Type_A) and
Put (X:in Type_B) "feel" like just overloaded functions - why do they
"need" an abstract parent Put (X : in General_T)?  Since General_T
is abstract, the latter Put will never be called.  Or have I missed the
point again!?


with Ada.Float_Text_IO;
with Ada.Integer_Text_IO;
with Ada.Text_IO;
procedure Dispatch is
   package P is
      type General_T;
      type Linked_List_T is access all General_T'Class;
      type General_T is abstract tagged record
         Next : Linked_List_T;
      end record;

      procedure Put (Item : in General_T) is abstract;

      type Type_A is new General_T with record
         Data : Integer;
      end record;

      procedure Put (Item : in Type_A);

      type Type_B is new General_T with record
         Data : Float;
      end record;

      procedure Put (Item : in Type_B);
   end P;


   package body P is
      procedure Put (Item : in Type_A) is
      begin
         Ada.Text_IO.Put ("A: ");
         Ada.Integer_Text_IO.Put (Item.Data);
      end Put;

      procedure Put (Item : in Type_B) is
      begin
         Ada.Text_IO.Put ("B: ");
         Ada.Float_Text_IO.Put (Item.Data);
      end Put;
   end P;

   use P;
   My_List : Linked_List_T := null;
begin
      --  generate some data
      My_List := new Type_A'(Next => My_List, Data => 100);
      My_List := new Type_B'(Next => My_List, Data => 64.5);
      My_List := new Type_A'(Next => My_List, Data => 12);
      My_List := new Type_B'(Next => My_List, Data => 100.0);

   --  print it all out - dispatcher will choose the right "Put".
   declare
      Iterator : Linked_List_T := My_List;
   begin
      while Iterator /= null loop
         Put (Iterator.all);
         Ada.Text_IO.New_Line;
         Iterator := Iterator.all.Next;
      end loop;
   end;
end Dispatch;




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

* Re: Instantiating private types with discriminants?
  2006-05-11  8:27                       ` rick H
@ 2006-05-11 10:28                         ` Dmitry A. Kazakov
  2006-05-11 15:59                           ` Robert A Duff
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-11 10:28 UTC (permalink / raw)


On Thu, 11 May 2006 08:27:18 GMT, rick H wrote:

> Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
>> On Wed, 10 May 2006 16:52:32 -0500, Rick H wrote:
>> 
>>> But all this has a side-effect that I haven't considered: the fact
>>> that a variable declared as a class-wide type *requires* initialization
>>> means that the compiler can determine the variable's type statically.
>> 
>> No. It only means that the type should be determinable at run-time. There
>> are may ways to do it. You can call a function with a class-wide result.
>> 
>> It might be especially interesting to take a look at S'Class'Input
>> attribute. It "dispatches" on a "bare" tag. [ Ada 2005 will offer a
>> mechanism for fully user-defined things like this. See
>> Generic_Dispatching_Constructor. ]
>> 
>>> This means that the following will raise a constraint error:
>>> declare
>>>    Var_A : General_T'Class := Get (10);  -- Type_A
>>> begin
>>>    Put (Var_A);
>>>    Ada.Text_IO.New_Line;
>>>    Var_A := Get (100.0);      -- Type_B - constraint error!
>>>    Put (Var_A);
>>> end;
>> 
>> Yes, but this is a different thing. You cannot change tag, either
>> dynamically or statically determined. Type mutators are no-no in Ada's type
>> system.
>> 
> Objects of class-wide types notwithstanding, I modified Ludovic's code
> example to change Type_A and Type_B into linked-list elements.  That
> meant no more objects of class-wide type,

You still have it the form of class-wide pointers (Linked_List_T).

[ You can't get rid of class-wide types if you what to dispatch. ]

> so I got rid of the Get
> definitions.  The following code does exactly what I wanted from this
> cut-down example: a list of either-Integer-or-Floats.  Next mystery:
> what's the use of abstract subprograms? Put (X:in Type_A) and
> Put (X:in Type_B) "feel" like just overloaded functions - why do they
> "need" an abstract parent Put (X : in General_T)?  Since General_T
> is abstract, the latter Put will never be called.  Or have I missed the
> point again!?

Puts might be overloaded in P, but it is rather a coincidence. More
important is that they override Put of General_T. This is what allows you
to dispatch to Put if you have some object of General_T'Class. Both
overloading and overriding are forms of polymorphism, so yes, in that sense
it is similar. The difference becomes obvious if you have moved
declarations of Type_A and Type_B into separate packages. Or if you have
defined a procedure to deeply put items:

procedure Put_Them_All (Item : General_T'Class) is
    Iterator : Linked_List_T := My_List;
begin
   Put (Item);                  -- Dispatches
   while Iterator /= null loop
      Put (Iterator.all);           -- Dispatches
      Ada.Text_IO.New_Line;
      Iterator := Iterator.Next;
   end loop;
end Put_Them_All;

This procedure will work with any type derived from General_T without even
knowing that any of them exist. This is so-called class-wide procedure - it
works for the class rather than for individual types of.

Note the a class-wide pointer like Linked_List_T dereferences to a
class-wide object (General_T'Class). This is why Put (X.all) dispatches. To
do this it does not need know anything else. [ Ada is a strongly typed
language. ]

A. Abstractness of a type statically ensures that no individual objects of
this type will ever exist.

B. Abstractness of a primitive subprogram statically ensures that it never
be called.

Any type with at least one abstract primitive subprogram has to be
abstract. The reverse is wrong.

You can consider declaration of:

   procedure Put (Item : in General_T) is abstract;

as an abbreviation for:

1. there is no Put defined on General_T
2. there is Put defined on General_T'Class (it dispatches)

If Put weren't abstract, then 1. would overload 2. That couldn't cause any
conflict because the types are different: General_T /= General_T'Class.

[...]
>   declare
>      Iterator : Linked_List_T := My_List;
>   begin
>      while Iterator /= null loop
>         Put (Iterator.all);
>         Ada.Text_IO.New_Line;
>         Iterator := Iterator.all.Next;

You don't need "all" here:

   Iterator := Iterator.Next;

Ada's pointers are transparent to record member/discriminant and array
element (indexing) access.

>      end loop;
>   end;

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-11 10:28                         ` Dmitry A. Kazakov
@ 2006-05-11 15:59                           ` Robert A Duff
  2006-05-12  7:37                             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Robert A Duff @ 2006-05-11 15:59 UTC (permalink / raw)


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

>...Both
> overloading and overriding are forms of polymorphism, so yes, in that sense
> it is similar. The difference becomes obvious if you have moved
> declarations of Type_A and Type_B into separate packages.

Right.  I suggest that "rick H" should rewrite his examples in the more
usual way -- one tagged type per package, and make them library
packages.

Then you can have a procedure that does dispatching calls to that
abstract subprogram, without having any visibility on the packages where
Type_A and Type_B are declared.

The abstract procedure is a place-holder -- the compiler needs to know
the parameter types and so forth.

> >         Iterator := Iterator.all.Next;
> 
> You don't need "all" here:
> 
>    Iterator := Iterator.Next;
> 
> Ada's pointers are transparent to record member/discriminant and array
> element (indexing) access.

Syntactically transparent, but not semantically.  If the ".all" syntax
weren't so darn ugly, I would recommend always using an explicit
dereference.  I believe there's an option in GNAT (a Restriction
pragma?) that allows you to force yourself to always say ".all",
which might not be a bad idea.

- Bob



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

* Re: Instantiating private types with discriminants?
  2006-05-11 15:59                           ` Robert A Duff
@ 2006-05-12  7:37                             ` Dmitry A. Kazakov
  2006-05-12  9:24                               ` Georg Bauhaus
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-12  7:37 UTC (permalink / raw)


On 11 May 2006 11:59:37 -0400, Robert A Duff wrote:

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

>> You don't need "all" here:
>> 
>>    Iterator := Iterator.Next;
>> 
>> Ada's pointers are transparent to record member/discriminant and array
>> element (indexing) access.
> 
> Syntactically transparent, but not semantically.  If the ".all" syntax
> weren't so darn ugly, I would recommend always using an explicit
> dereference.  I believe there's an option in GNAT (a Restriction
> pragma?) that allows you to force yourself to always say ".all",
> which might not be a bad idea.

I beg to disagree. Semantic difference you are talking about is not always
the problem space's one. It is often just a language artefact. In my view,
exactly the opposite should be done, i.e. removing all traces of "all".
(:-)) A user-defined pointer (referential) type should (IMO) be considered
a subtype, that inherits all operations of the target type. If assignment
gets overridden (to have shallow copy semantics), that does not
automatically imply anything for other operations.

Also, a good design should have as a goal preventing visibility of both the
target type if its referential counterpart in client contexts. So if
references are needed then the target type should be private. That would
exclude any misunderstandings about semantics of a given operation.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-12  7:37                             ` Dmitry A. Kazakov
@ 2006-05-12  9:24                               ` Georg Bauhaus
  2006-05-12 12:40                                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Georg Bauhaus @ 2006-05-12  9:24 UTC (permalink / raw)


On Fri, 2006-05-12 at 09:37 +0200, Dmitry A. Kazakov wrote:


> I beg to disagree. Semantic difference you are talking about is not always
> the problem space's one. It is often just a language artefact. In my view,
> exactly the opposite should be done, i.e. removing all traces of "all".
> (:-)) A user-defined pointer (referential) type should (IMO) be considered
> a subtype, that inherits all operations of the target type. If assignment
> gets overridden (to have shallow copy semantics), that does not
> automatically imply anything for other operations.

Will the programmer have a chance to express whether or not
he or she is effectively creating an alias?

Eiffel addresses this in a reversed way, I think. You can write
"expanded" before a class definition, or before the type name
in a feature declaration. So no ".all" either, but at least
a trace of objects versus references.






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

* Re: Instantiating private types with discriminants?
  2006-05-12  9:24                               ` Georg Bauhaus
@ 2006-05-12 12:40                                 ` Dmitry A. Kazakov
  2006-05-12 18:25                                   ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2006-05-12 12:40 UTC (permalink / raw)


On Fri, 12 May 2006 11:24:52 +0200, Georg Bauhaus wrote:

> On Fri, 2006-05-12 at 09:37 +0200, Dmitry A. Kazakov wrote:
> 
>> I beg to disagree. Semantic difference you are talking about is not always
>> the problem space's one. It is often just a language artefact. In my view,
>> exactly the opposite should be done, i.e. removing all traces of "all".
>> (:-)) A user-defined pointer (referential) type should (IMO) be considered
>> a subtype, that inherits all operations of the target type. If assignment
>> gets overridden (to have shallow copy semantics), that does not
>> automatically imply anything for other operations.
> 
> Will the programmer have a chance to express whether or not
> he or she is effectively creating an alias?

The result is typed, that should express it. Ada is very strict about 
types.

> Eiffel addresses this in a reversed way, I think. You can write
> "expanded" before a class definition, or before the type name
> in a feature declaration. So no ".all" either, but at least
> a trace of objects versus references.

Ada has "access qualifier" producing anonymous access type. I don't like 
anonymous types much. They require structural types equivalence.

BTW, if pointer were a subtype, then you wouldn't need hard-wired 
dispatching on "access T", because the pointer will simply inherit 
primitive operations and thus dispatch.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Instantiating private types with discriminants?
  2006-05-12 12:40                                 ` Dmitry A. Kazakov
@ 2006-05-12 18:25                                   ` Randy Brukardt
  0 siblings, 0 replies; 34+ messages in thread
From: Randy Brukardt @ 2006-05-12 18:25 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:1dwuemv53sbgw$.1pgg57qa5hsdr.dlg@40tude.net...
...
> BTW, if pointer were a subtype, then you wouldn't need hard-wired
> dispatching on "access T", because the pointer will simply inherit
> primitive operations and thus dispatch.

I vaguely remember that Ada 9X did it that way at one point. It didn't work
out for some reason (too many preference rules needed?).

                            Randy.





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

end of thread, other threads:[~2006-05-12 18:25 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-09 13:17 Instantiating private types with discriminants? rick H
2006-05-09 13:45 ` Georg Bauhaus
2006-05-09 14:06   ` rick H
2006-05-09 13:56 ` Ludovic Brenta
2006-05-09 14:24   ` rick H
2006-05-09 19:48     ` Ludovic Brenta
2006-05-09 14:05 ` Dmitry A. Kazakov
2006-05-09 14:48 ` rick H
2006-05-09 15:20   ` Jerry Petrey
2006-05-09 15:42     ` rick H
2006-05-09 15:53   ` Avoiding use Ada.Tags (was Re: Instantiating private types with discriminants?) Alex R. Mosteo
2006-05-09 16:01   ` Instantiating private types with discriminants? Dmitry A. Kazakov
2006-05-10  7:42     ` rick H
2006-05-10  9:09       ` Ludovic Brenta
2006-05-10 11:49         ` Georg Bauhaus
2006-05-10 13:44         ` rick H
2006-05-10 14:21           ` Ludovic Brenta
2006-05-10 15:10             ` rick H
2006-05-10 15:45               ` Ludovic Brenta
2006-05-10 14:41           ` Dmitry A. Kazakov
2006-05-10 15:34             ` rick H
2006-05-10 19:01               ` Georg Bauhaus
2006-05-10 19:05                 ` Ludovic Brenta
2006-05-10 21:52                   ` Rick H
2006-05-11  1:17                     ` Jeffrey R. Carter
2006-05-11  7:44                     ` Dmitry A. Kazakov
2006-05-11  8:27                       ` rick H
2006-05-11 10:28                         ` Dmitry A. Kazakov
2006-05-11 15:59                           ` Robert A Duff
2006-05-12  7:37                             ` Dmitry A. Kazakov
2006-05-12  9:24                               ` Georg Bauhaus
2006-05-12 12:40                                 ` Dmitry A. Kazakov
2006-05-12 18:25                                   ` Randy Brukardt
2006-05-09 19:57   ` "Use" and "=" for Tags (was: Re: Instantiating private types with discriminants?) Jeffrey R. Carter

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