comp.lang.ada
 help / color / mirror / Atom feed
* I'm facing an issue with: call to abstract procedure must be dispatching
@ 2015-12-08 16:45 Serge Robyns
  2015-12-08 16:59 ` G.B.
                   ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 16:45 UTC (permalink / raw)


Gents,

I've been trying to get my head around a way to create a solution where the storage method can be changed.  For this I'm using Ada's interface language construct.  So far I've only be successful with the following construct, whereas store1, store2, etc are implementations to store object1, object2, etc.  These are created as child packages of their respective objects so they can access the private parts.

   type T_Abstract_Data_Store is limited interface
     and Store1_Interface
     and Store2_Interface
     ...

In the implementation "class"

   type T_Data_Store is limited new T_Abstract_Data_Store with record
      Store1 : T_Store1;
      Store2 : T_Store2;
      ...

This then requires to write glue code to implement a link to the different implementations of the stores. 

procedure Insert (Store : in out T_Data_Store;
                  Obj   : in     T_XYZ'Class)
is
begin
    Store.Store1.Insert (Obj);
end Insert;

This could have been avoided if Ada would have allowed some form of multi-inheritance of in the instance of a multi-interface "class".

Now I've been trying to resolve this through another mean but I'm now facing the "call to abstract procedure must be dispatching" error message which I cannot explain nor resolve.

Below an a sample compile-able piece of code.

package Clients is
      -- could also be in private part ....
   type T_Client is tagged record
      ID : Natural;
   end record;
end Clients;

with Clients; use Clients;

package Abstract_Store is
   type T_Asbtract_Store is limited interface;

   procedure Insert (Store  : in out T_Asbtract_Store;
                     Client : in     T_Client'Class)
   is abstract;
   -- actually in my code Client will be a subclass of T_Client, for example
   -- T_Client_DAO'Class
end Abstract_Store;

with Clients; use Clients;
with Abstract_Store; use Abstract_Store;

procedure test is

   type T_Store is new T_Asbtract_Store with null record;
   
   overriding procedure Insert (Store  : in out T_Store;
                                Client : in     T_Client'Class) is
   begin
      null;
   end Insert;
   
   A_Store   : access T_Asbtract_Store;
   The_Store : aliased T_Store;
   Client    : T_Client;

begin
   A_Store := T_Asbtract_Store (The_Store)'Access;
   A_Store.Insert (Client);
end test;

The compiler does not like "A_Store.Insert (Client)".
Also with this construct I've to use an access variable ...

Anyone can shed a light on this or suggest a smarter Ada way?

Regards,
Serge

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns
@ 2015-12-08 16:59 ` G.B.
  2015-12-08 17:04 ` Dmitry A. Kazakov
  2015-12-08 17:30 ` Jeffrey R. Carter
  2 siblings, 0 replies; 34+ messages in thread
From: G.B. @ 2015-12-08 16:59 UTC (permalink / raw)


On 08.12.15 17:45, Serge Robyns wrote:

> procedure test is
>
>     type T_Store is new T_Asbtract_Store with null record;
>
>     overriding procedure Insert (Store  : in out T_Store;
>                                  Client : in     T_Client'Class) is
>     begin
>        null;
>     end Insert;
>
>     A_Store   : access T_Asbtract_Store;
>     The_Store : aliased T_Store;
>     Client    : T_Client;
>
> begin
>     A_Store := T_Asbtract_Store (The_Store)'Access;
>     A_Store.Insert (Client);
> end test;
>
> The compiler does not like "A_Store.Insert (Client)".
> Also with this construct I've to use an access variable ...
>
> Anyone can shed a light on this or suggest a smarter Ada way?

Just dispatch:

procedure test is

    type T_Store is new T_Asbtract_Store with null record;

    overriding procedure Insert (Store  : in out T_Store;
                                 Client : in     T_Client'Class) is
    begin
       null;
    end Insert;

    procedure Wanting_Some_Store
      (A_Store  : in out T_Asbtract_Store'Class;
       A_Client :        T_client) is
    begin
       A_Store.Insert (A_Client);
    end Wanting_Some_Store;

    Client    : T_Client;
    The_Store : T_Store;
begin
    Wanting_Some_Store (The_Store, Client);
end test;




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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns
  2015-12-08 16:59 ` G.B.
@ 2015-12-08 17:04 ` Dmitry A. Kazakov
  2015-12-08 17:24   ` Serge Robyns
  2015-12-08 17:30 ` Jeffrey R. Carter
  2 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-08 17:04 UTC (permalink / raw)


On Tue, 8 Dec 2015 08:45:52 -0800 (PST), Serge Robyns wrote:

[...]

> procedure test is
>    type T_Store is new T_Asbtract_Store with null record;
>    overriding procedure Insert (Store  : in out T_Store;
>                                 Client : in     T_Client'Class) is
>    begin
>       null;
>    end Insert;
>    
>    A_Store   : access T_Asbtract_Store;
>    The_Store : aliased T_Store;
>    Client    : T_Client;
> begin
>    A_Store := T_Asbtract_Store (The_Store)'Access;
>    A_Store.Insert (Client);
> end test;

No idea what you are trying to achieve, but the above should have been:

   The_Store : aliased T_Store;
   Client : T_Client;
begin
   The_Store.Insert  (Client);

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


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:04 ` Dmitry A. Kazakov
@ 2015-12-08 17:24   ` Serge Robyns
  2015-12-08 17:42     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 17:24 UTC (permalink / raw)


On Tuesday, 8 December 2015 18:05:09 UTC+1, Dmitry A. Kazakov  wrote:

>    The_Store : aliased T_Store;
>    Client : T_Client;
> begin
>    The_Store.Insert  (Client);

Yes, I know this does work.  I did compile the example first like that.  But this is an attempt to "simplify" the problem to post it here.  What really happens is the following.

In the main package I'm actually calling other packages passing the The_Store. For example:

-- with Store.Gnatcoll_version;
with Store.Inmemory_Container_version;

Both implement the same interface but with different underlying storage.
In the case of in_memory, I'll keep containers inside the type T_Data_Store
and in case of Gnatcoll some DB parameters inside T_Data_Store
and then ...

   Do_Something (The_Store);

The signature of Do_Something is
   procedure Do_Something (A_Store : in out T_Abstract_Store);

Inside that procedure I'm using the A_Store.  This regardless how A_Store got implemented.

However all "users" will just call "A_Store.Insert (Something);" or "Something := A_Store.Get (ID);", etc.

If I've to use the "real" instance, I've to edit all packages that "with"'s depending on the case.  In some languages this could controlled by a pre-processor directive.  Any other suggestion?

Serge


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns
  2015-12-08 16:59 ` G.B.
  2015-12-08 17:04 ` Dmitry A. Kazakov
@ 2015-12-08 17:30 ` Jeffrey R. Carter
  2015-12-08 17:48   ` Serge Robyns
  2 siblings, 1 reply; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-08 17:30 UTC (permalink / raw)


On 12/08/2015 09:45 AM, Serge Robyns wrote:
> 
> I've been trying to get my head around a way to create a solution where the storage method can be changed.

In your approach, IIUC, you would change the Insert procedures for each of the
types to change the storage method. You are on the right track, but are confused
by the added complexity of using programming by extension. The typical way to do
this in Ada is something like

with T1_Ops;
with T2_Ops;
...
package Storage is
   procedure Put (Item : in T1_Ops.T1);

   procedure Put (Item : in T2_Ops.T2);

   ...
end Storage;

The bodies of the Put procedures correspond to your Insert procedures. (You can
call them Insert, too, if you like, but I once heard it asserted that a
correctly designed Ada program has 90% of its operations named Put or Get.)

This is then used as

with Storage;

...

Storage.Put (Item => Current_Item);

which is the same regardless of the type of the item being stored. To change the
storage method, you change the body of Storage.

Note that programming by extension is neither necessary nor desirable for this.

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:24   ` Serge Robyns
@ 2015-12-08 17:42     ` Dmitry A. Kazakov
  2015-12-08 18:00       ` Serge Robyns
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-08 17:42 UTC (permalink / raw)


On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote:

> On Tuesday, 8 December 2015 18:05:09 UTC+1, Dmitry A. Kazakov  wrote:
> 
>>    The_Store : aliased T_Store;
>>    Client : T_Client;
>> begin
>>    The_Store.Insert  (Client);
> 
> Yes, I know this does work.  I did compile the example first like that.
>  But this is an attempt to "simplify" the problem to post it here.  What
> really happens is the following.
> 
> In the main package I'm actually calling other packages passing the
> The_Store. For example:
> 
> -- with Store.Gnatcoll_version;
> with Store.Inmemory_Container_version;
> 
> Both implement the same interface but with different underlying storage.
> In the case of in_memory, I'll keep containers inside the type T_Data_Store
> and in case of Gnatcoll some DB parameters inside T_Data_Store
> and then ...
> 
>    Do_Something (The_Store);
> 
> The signature of Do_Something is
>    procedure Do_Something (A_Store : in out T_Abstract_Store);

In which relation is Do_Something to T_Abstract_Store?

1. a primitive operation =
      has a separate implementation for each concrete type (from the class)
2. class-wide operation =
      has same implementation for all types (from the class)

(anything else is a design fault)

> Inside that procedure I'm using the A_Store. This regardless how A_Store
> got implemented.

That sounds like #2. Then it must be declared as:

procedure Do_Something (A_Store : in out T_Abstract_Store'Class);

> However all "users" will just call "A_Store.Insert (Something);" or
> "Something := A_Store.Get (ID);", etc.

You lost me here. What has this to do with Do_Something?

> If I've to use the "real" instance, I've to edit all packages that
> "with"'s depending on the case.  In some languages this could controlled
> by a pre-processor directive.  Any other suggestion?

I don't understand the problem. You are trying to explain a [failed]
solution of some problem. I would suggest you to start with the problem.

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


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:30 ` Jeffrey R. Carter
@ 2015-12-08 17:48   ` Serge Robyns
  2015-12-08 18:46     ` Jeffrey R. Carter
  2015-12-08 22:20     ` Simon Wright
  0 siblings, 2 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 17:48 UTC (permalink / raw)


On Tuesday, 8 December 2015 18:30:59 UTC+1, Jeffrey R. Carter  wrote:
> with T1_Ops;
> with T2_Ops;
> ...
> package Storage is
>    procedure Put (Item : in T1_Ops.T1);
> 
>    procedure Put (Item : in T2_Ops.T2);
> 
>    ...
> end Storage;
> 
> The bodies of the Put procedures correspond to your Insert procedures. (You can
> call them Insert, too, if you like, but I once heard it asserted that a
> correctly designed Ada program has 90% of its operations named Put or Get.)
> 
> This is then used as
> 
> with Storage;
> 
> ...
> 
> Storage.Put (Item => Current_Item);
> 
> which is the same regardless of the type of the item being stored. To change the
> storage method, you change the body of Storage.

This seems indeed a workable solution.  Actually, in my initial version I had one big package for the implementation.  I found this "ugly" and wanted to decimate them as per package under the form of child packages.  This also allows me to use private types, as children can access private elements.

My question is then, how can I easily manage this from a source management perspective.  As I said in a prior post, changing the "with/use" everywhere or use some file system "tricks" to fool the compiler to pick the "right" version.
Maybe there is some Ada "feature" to do this nicely.  As I said in a prior post, in C and C++ I would use the pre-processor to pick the "right" one.

> Note that programming by extension is neither necessary nor desirable for this.

I've noticed from your papers you generally disapproves "programming by extension".

Serge


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:42     ` Dmitry A. Kazakov
@ 2015-12-08 18:00       ` Serge Robyns
  2015-12-08 18:22         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 18:00 UTC (permalink / raw)


On Tuesday, 8 December 2015 18:42:26 UTC+1, Dmitry A. Kazakov  wrote:
> On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote:
> In which relation is Do_Something to T_Abstract_Store?
> 
> 1. a primitive operation =
>       has a separate implementation for each concrete type (from the class)
> 2. class-wide operation =
>       has same implementation for all types (from the class)
> 
> (anything else is a design fault)
> 
> > Inside that procedure I'm using the A_Store. This regardless how A_Store
> > got implemented.
> 
> That sounds like #2. Then it must be declared as:

This is indeed Correct.  Thanks for the tip.  Now it works, well at least it compiles .....

procedure test is

   type T_Store is new T_Asbtract_Store with null record;

   overriding procedure Insert (Store  : in out T_Store;
                                Client : in     T_Client'Class) is
   begin
      null;
   end Insert;
   
   procedure Do_Something (A_Store : in out T_Asbtract_Store'Class)
   is
      Client    : T_Client;
   begin
      A_Store.Insert (Client);
   end Do_Something;        
   

   A_Store   : access T_Asbtract_Store;
   The_Store : aliased T_Store;

begin
   A_Store := T_Asbtract_Store (The_Store)'Access;
   Do_Something (A_Store.all);
end test;

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 18:00       ` Serge Robyns
@ 2015-12-08 18:22         ` Dmitry A. Kazakov
  2015-12-08 20:21           ` Serge Robyns
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-08 18:22 UTC (permalink / raw)


On Tue, 8 Dec 2015 10:00:24 -0800 (PST), Serge Robyns wrote:

> On Tuesday, 8 December 2015 18:42:26 UTC+1, Dmitry A. Kazakov  wrote:
>> On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote:
>> In which relation is Do_Something to T_Abstract_Store?
>> 
>> 1. a primitive operation =
>>       has a separate implementation for each concrete type (from the class)
>> 2. class-wide operation =
>>       has same implementation for all types (from the class)
>> 
>> (anything else is a design fault)
>> 
>>> Inside that procedure I'm using the A_Store. This regardless how A_Store
>>> got implemented.
>> 
>> That sounds like #2. Then it must be declared as:
> 
> This is indeed Correct.  Thanks for the tip.  Now it works, well at least it compiles .....
> 
> procedure test is
> 
>    type T_Store is new T_Asbtract_Store with null record;
> 
>    overriding procedure Insert (Store  : in out T_Store;
>                                 Client : in     T_Client'Class) is
>    begin
>       null;
>    end Insert;
>    
>    procedure Do_Something (A_Store : in out T_Asbtract_Store'Class)
>    is
>       Client    : T_Client;
>    begin
>       A_Store.Insert (Client);
>    end Do_Something;        
>    
>    A_Store   : access T_Asbtract_Store;

Observe that exactly same logic applies here. You never ever use a
type-specific access type when you deal with whole class. Thus, without any
further considerations it must be this:

   A_Store : access T_Asbtract_Store'Class;

<rant on> In each instance you should clearly understand whether the code
deals with one specific type or with all class. Once you know that, you
express this knowledge in terms of types. One of the major Ada advantages
with respect to OO is that it is properly typed. The type of a specific
instance and the type of all instances from the class are two *different*
types. The language rejected your program not because of being cruel, but
because you used one type as if it were another. <rant off>

>    The_Store : aliased T_Store;
> 
> begin
>    A_Store := T_Asbtract_Store (The_Store)'Access;

Now, of course no explicit type conversion required:

  A_Store := The_Store'Access;

(Type conversion must raise alarm bells)

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


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:48   ` Serge Robyns
@ 2015-12-08 18:46     ` Jeffrey R. Carter
  2015-12-08 20:28       ` Serge Robyns
  2015-12-08 22:20     ` Simon Wright
  1 sibling, 1 reply; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-08 18:46 UTC (permalink / raw)


On 12/08/2015 10:48 AM, Serge Robyns wrote:
> 
> This seems indeed a workable solution.  Actually, in my initial version I had
> one big package for the implementation.  I found this "ugly" and wanted to
> decimate them as per package under the form of child packages.  This also
> allows me to use private types, as children can access private elements.

The point of this approach is that changing the storage method only requires
changing one thing in the entire system. If you need to store private
information then the storage operation needs to be part of the implementation of
the thing with private information, and you lose this advantage. You have to
consider the trade off between the values of keeping the information private and
keeping the storage method in one place.

> My question is then, how can I easily manage this from a source management
> perspective.  As I said in a prior post, changing the "with/use" everywhere
> or use some file system "tricks" to fool the compiler to pick the "right"
> version. Maybe there is some Ada "feature" to do this nicely.  As I said in a
> prior post, in C and C++ I would use the pre-processor to pick the "right"
> one.

I would think that one would use a storage method for a while, and if it proved
unsuitable, switch to another. In such a case, one would simply modify the body
of the pkg, and there would be no source-management issues. You would use your
version-management system (VCS) to revert to the earlier version of that single
file if you decide the change was a mistake.

What you're describing sounds more like maintaining multiple versions that
differ only in their storage method. VCSs don't help with this; generally the
only way to use a VCS to address this is to have multiple branches that differ
only in the 1 file. and merge non-storage changes from one branch to the others.
That can get as messy and hard to do right as having all the implementations in
a single file with all the preprocessor directives for selecting the right one.
Usually additional "make" tools are used to help get it right.

One approach in Ada is to have to body files, and copy the desired one to the
correct name prior to building the system. If the body file name is
Storage.Ada2, for example, you might have bodies Storage_M1.Ada2,
Storage_M2.Ada2, ..., one of which you'd copy to Storage.Ada2 prior to building.
You might use scripts or another "make" tool to reduce the chances of getting it
wrong.

Some people like to use a library-level renaming in cases like this:

with Storage_M2;
package Storage renames Storage_M2;

changing the withed pkg prior to building, but that involves keeping multiple
copies of an identical pkg spec, which is itself a maintenance problem.

Your attempted solution using multiple withs, with all but one commented out, is
similar to this, as the multiple pkgs would all have identical specs. In
addition, you would need at least 1 further change in the unit that withs the
storage pkg.

> I've noticed from your papers you generally disapproves "programming by
> extension".

I investigated it extensively over 2 decades ago and concluded that its negative
qualities outweighed any positive ones. I have not seen anything since to change
my mind. Kazakov, on the other hand, likes multiple inheritance, so you have his
balancing view posting here.

In addition to my papers at

http://pragmada.x10hosting.com/papers.html

you might also be interested in Rosen's paper "What Orientation Should Ada
Objects Take", available at

http://www.adalog.fr/en/publications.html

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 18:22         ` Dmitry A. Kazakov
@ 2015-12-08 20:21           ` Serge Robyns
  2015-12-08 21:08             ` Dmitry A. Kazakov
  2015-12-08 22:55             ` Jeffrey R. Carter
  0 siblings, 2 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 20:21 UTC (permalink / raw)


At times it proves to be helpful to write a sample test to clear away the clutter.  Actually the whole issue appeared because I've been introducing the members to the T_Abstract_Data_Store as following:

   type T_Abstract_Data_Store is abstract tagged limited record
      Clients: access T_Abstract_Client;  -- was missing 'Class
      Store2 : access T_Abstract_Store2;  -- was missing 'Class
   ...

Before it was a limited interface and hence no record.  This reminds me I haven't yet fully figured out when it is <b>best</b> use limited and when not for my cases.  So far I can use limited as I don't need the assignment for T_Store.  Likewise from you here I try to avoid access variable as much as possible but at times I've no choice, well at least I use programming by extension ...

My real Do_Something was already having A_Store : in out T_A..._S...'Class) however I was using
     A_Store.Clients.Insert (Client);
I've been looking and re-looking at "A_Store" whilst the problem was actually "Clients", which was lacking 'Class;  I've been misled as I got so used that the Ada compiler is very good at error handling, so much better than g++ or maybe I didn't read the column number properly (shame on me!).

Now to go back to the overall solution.  This is one of the rare cases where multiple inheritance would have made things easier, as then the non abstract class would "inherit" from the parents the respective implementations like in C++ class T_Store : public T_Store1, public T_Store2, ....  Then I could just write "A_Store.Insert (Client);" as this was my original code as I had "glue" code in the body of T_Store package, overriding all the interfaces.

I know that this statement will spark some discussions between the two "camps" as per Jeffrey's comment.

Yes, the Ada compiler and language is very cruel but I learned to appreciate because often, if not always, highlights (potential) bugs lurking around.  In other languages they don't distinguish between class wide and non class wide, whereas Ada is very picky about it.  This is what keeps me trying to pursue with Ada, even if the latest C++ offers a few nice things without getting it right although.  Such as for example; "typedef enum class int some_id;" that equates to "type some_id : Integer;" instead of "typedef some_id int;" that equates to "subtype some_id : Integer;"  and dynamic_cast<class_xyz*> which performs run-time conversion checks just like Ada does (except it creates a null pointer and Ada an understandable exception) and by which I got already caught once.

My only long term concern is how many people can program in Ada?  Who will later be able to maintain my code?  I've high respect to people like Dmitry, David and others who are putting a lot of their time maintaining various projects.  But this could become my "killing" concern.  I recall reading someone writing he use Ada for his prototyping/design and then implementing it the target language.  I could follow that line of thought as Ada is really helping me to get things "right" but I don't feel like rewriting 1000's of code.

That last paragraph sounds almost like a "message in a bottle" ....

Serge


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 18:46     ` Jeffrey R. Carter
@ 2015-12-08 20:28       ` Serge Robyns
  0 siblings, 0 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 20:28 UTC (permalink / raw)


On Tuesday, 8 December 2015 19:46:10 UTC+1, Jeffrey R. Carter  wrote:
> I investigated it extensively over 2 decades ago and concluded that its negative
> qualities outweighed any positive ones. I have not seen anything since to change
> my mind. Kazakov, on the other hand, likes multiple inheritance, so you have his
> balancing view posting here.
> 
> In addition to my papers at
> 
> http://pragmada.x10hosting.com/papers.html
> 
> you might also be interested in Rosen's paper "What Orientation Should Ada
> Objects Take", available at
> 
> http://www.adalog.fr/en/publications.html
> 

Thank you for the references, when time permits I'll read time as I'm still trying to get through the very boring ALRM 2012.  When I was "professionally" programming a couple of decades ago it was C, Pascal and assembler.  Hence I do lack personal evidence to make an assessment but the ideas of programming by extension seems appealing to me.

And indeed I noticed indeed that you and Dmitry are on the opposite side however sharing the same love for Ada.

Serge

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 20:21           ` Serge Robyns
@ 2015-12-08 21:08             ` Dmitry A. Kazakov
  2015-12-08 21:55               ` Serge Robyns
  2015-12-09 10:53               ` G.B.
  2015-12-08 22:55             ` Jeffrey R. Carter
  1 sibling, 2 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-08 21:08 UTC (permalink / raw)


On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote:

> At times it proves to be helpful to write a sample test to clear away the
> clutter.  Actually the whole issue appeared because I've been introducing
> the members to the T_Abstract_Data_Store as following:
> 
>    type T_Abstract_Data_Store is abstract tagged limited record
>       Clients: access T_Abstract_Client;  -- was missing 'Class
>       Store2 : access T_Abstract_Store2;  -- was missing 'Class
>    ...

If you really need mix-in then better do it like this:

   type T_Abstract_Data_Store
      (  Clients : not null access T_Abstract_Client'Class;
         Store2 : not null access T_Abstract_Store2'Class
      )  is abstract tagged limited record

This would add a bit more safety, much needed when using access types.

> Now to go back to the overall solution.  This is one of the rare cases
> where multiple inheritance would have made things easier, as then the non
> abstract class would "inherit" from the parents the respective
> implementations like in C++ class T_Store : public T_Store1, public
> T_Store2, ....  Then I could just write "A_Store.Insert (Client);" as this
> was my original code as I had "glue" code in the body of T_Store package,
> overriding all the interfaces.

Why MI is needed here? Of which of two stores is Insert?

There is an ugly method of achieving poor-man's full MI. Full means that
you inherit multiple implementations rather than only interfaces. The
latter is allowed in Ada. The method is that you inherit one implementation
normally and add others through a generic. I don't want to describe it,
because it is really ugly as anything that involves generics. I am not sure
you need full MI in first place.

> I know that this statement will spark some discussions between the two
> "camps" as per Jeffrey's comment.

There are people in c.l.a who believe in procedural decomposition and
inference from the type structure. It is incompatible with fundamental
Ada's principles of strong typing and separation of interface and
implementation. But that is my opinion.

> My only long term concern is how many people can program in Ada?

If you do, that is one man more... Any language lives by people who want to
use it.

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


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 21:08             ` Dmitry A. Kazakov
@ 2015-12-08 21:55               ` Serge Robyns
  2015-12-08 22:43                 ` Serge Robyns
  2015-12-09  8:48                 ` Dmitry A. Kazakov
  2015-12-09 10:53               ` G.B.
  1 sibling, 2 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 21:55 UTC (permalink / raw)


On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov  wrote:
> On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote:
> 
> If you really need mix-in then better do it like this:
> 
>    type T_Abstract_Data_Store
>       (  Clients : not null access T_Abstract_Client'Class;
>          Store2 : not null access T_Abstract_Store2'Class
>       )  is abstract tagged limited record
> 
> This would add a bit more safety, much needed when using access types.

Was considering this option, I haven't used this construct yet.  This is a place where reading the ALRM just gives headaches without a clue of the use cases.  And guess what, I just have been hit by a forgotten assignment ....  Then it really feels like C and the null pointers :-(  So this is my next immediate step.  Now I'm still fixing a "tag check failed" :-P, which was one thing a mentioned an hour ago.  What I do like with Ada, is that I can do major refactoring and regularly on the first successful compile have a "bug free" version.  Now with these access variables the story changes a little, I got hit by my first segmentation violation in Ada :-).


> Why MI is needed here? Of which of two stores is Insert?

Just stupid laziness ....

> > My only long term concern is how many people can program in Ada?
> 
> If you do, that is one man more... Any language lives by people who want to
> use it.
> 

Yes, I try to advocate and hence show by the example ... but I'm not planning being a lonesome cowboy ....

Regards,

Serge


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 17:48   ` Serge Robyns
  2015-12-08 18:46     ` Jeffrey R. Carter
@ 2015-12-08 22:20     ` Simon Wright
  1 sibling, 0 replies; 34+ messages in thread
From: Simon Wright @ 2015-12-08 22:20 UTC (permalink / raw)


Serge Robyns <serge.robyns@gmail.com> writes:

> My question is then, how can I easily manage this from a source
> management perspective.

With GNAT/GNAT Project, there's package Naming: roughly,

   package Naming is
     case Option is
       when "simple" =>
          for Body ("Pack.Child") use "pack-child-simple.adb";
       when "complex" =>
          for Body ("Pack.Child") use "pack-child-complex.adb";
     end case;
   end Naming;

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 21:55               ` Serge Robyns
@ 2015-12-08 22:43                 ` Serge Robyns
  2015-12-08 22:55                   ` Jeffrey R. Carter
  2015-12-09  8:48                 ` Dmitry A. Kazakov
  1 sibling, 1 reply; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 22:43 UTC (permalink / raw)


On Tuesday, 8 December 2015 22:55:33 UTC+1, Serge Robyns  wrote:
> On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov  wrote:
> > On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote:
> > 
> > If you really need mix-in then better do it like this:
> > 
> >    type T_Abstract_Data_Store
> >       (  Clients : not null access T_Abstract_Client'Class;
> >          Store2 : not null access T_Abstract_Store2'Class
> >       )  is abstract tagged limited record
> > 
> > This would add a bit more safety, much needed when using access types.
> 

Done, everything is completed and runs.  The tag error was a "hidden" bug where I didn't return the right child class.  Once I got around the issue the changes went quickly through and I would have had only one bug if I had used the above construct immediately as the compiler wouldn't allowed me to "forget".  This is what amazes me with Ada.

Now I've a generic question with regards to access type vs. in-out for parameters.  What is the proper Ada way to pass the "Store" around?
Is it:
Procedure Do_Something (A_Store : in out T_Abstract_Store'Class);
or:
Procedure Do_Something (A_Store : not null access T_Abstract_Store'Class);
?

As a former C programmer my habit goes for the not null access variant.

Regards,
Serge


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 22:43                 ` Serge Robyns
@ 2015-12-08 22:55                   ` Jeffrey R. Carter
  2015-12-09 23:03                     ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-08 22:55 UTC (permalink / raw)


On 12/08/2015 03:43 PM, Serge Robyns wrote:
> 
> Now I've a generic question with regards to access type vs. in-out for parameters.  What is the proper Ada way to pass the "Store" around?
> Is it:
> Procedure Do_Something (A_Store : in out T_Abstract_Store'Class);
> or:
> Procedure Do_Something (A_Store : not null access T_Abstract_Store'Class);
> ?
> 
> As a former C programmer my habit goes for the not null access variant.

As an Ada S/W engineer, you should use "in out". With "in out", the compiler
does the work of passing the object in such a way that the operation can read
and modify the value. With an access, you (or whoever calls your operation) has
to do extra work to have an access value to pass. It's unnecessary extra work
and an extra chance to get something wrong, and so to be avoided.

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 20:21           ` Serge Robyns
  2015-12-08 21:08             ` Dmitry A. Kazakov
@ 2015-12-08 22:55             ` Jeffrey R. Carter
  2015-12-08 23:04               ` Serge Robyns
  1 sibling, 1 reply; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-08 22:55 UTC (permalink / raw)


On 12/08/2015 01:21 PM, Serge Robyns wrote:
> 
> Before it was a limited interface and hence no record.  This reminds me I
> haven't yet fully figured out when it is <b>best</b> use limited and when not
> for my cases.  So far I can use limited as I don't need the assignment for
> T_Store.  Likewise from you here I try to avoid access variable as much as
> possible but at times I've no choice, well at least I use programming by
> extension ...

If you don't need assignment because there will never be more than one of them,
that's an indication that you don't need a type and a variable; you need a
module. I suspect you think in terms of types because C++ only provides
encapsulation and information hiding through its class construct. In Ada, your
1st thought when you want encapsulation or information hiding should be a pkg.

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 22:55             ` Jeffrey R. Carter
@ 2015-12-08 23:04               ` Serge Robyns
  2015-12-08 23:42                 ` Jeffrey R. Carter
                                   ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Serge Robyns @ 2015-12-08 23:04 UTC (permalink / raw)


On Tuesday, 8 December 2015 23:55:52 UTC+1, Jeffrey R. Carter  wrote:
> On 12/08/2015 01:21 PM, Serge Robyns wrote:
> > 
> > Before it was a limited interface and hence no record.  This reminds me I
> > haven't yet fully figured out when it is <b>best</b> use limited and when not
> > for my cases.  So far I can use limited as I don't need the assignment for
> > T_Store.  Likewise from you here I try to avoid access variable as much as
> > possible but at times I've no choice, well at least I use programming by
> > extension ...
> 
> If you don't need assignment because there will never be more than one of them,
> that's an indication that you don't need a type and a variable; you need a
> module. I suspect you think in terms of types because C++ only provides
> encapsulation and information hiding through its class construct. In Ada, your
> 1st thought when you want encapsulation or information hiding should be a pkg.
> 

How do I then pass the context?  I mean, now I pass the parameter around.  through global variables?  Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session.  I do believe these are good reasons for having a variable.

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 23:04               ` Serge Robyns
@ 2015-12-08 23:42                 ` Jeffrey R. Carter
  2015-12-09 13:40                 ` Jere
  2015-12-13 21:37                 ` Shark8
  2 siblings, 0 replies; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-08 23:42 UTC (permalink / raw)


On 12/08/2015 04:04 PM, Serge Robyns wrote:
> 
> How do I then pass the context?  I mean, now I pass the parameter around.
> through global variables?  Also in the future I would like to have multiple
> "tasks" running, each with their "context", especially the db session.  I do
> believe these are good reasons for having a variable.

The case I was saying should be a pkg is when there's only 1. In that case, the
state is in the pkg body. Why should the rest of the code need to pass anything
around?

For multiple tasks with independent states, then clearly something needs to be
passed in. Whether that's the entire state or something that lets the pkg obtain
the correct state is a design decision.

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 21:55               ` Serge Robyns
  2015-12-08 22:43                 ` Serge Robyns
@ 2015-12-09  8:48                 ` Dmitry A. Kazakov
  1 sibling, 0 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-09  8:48 UTC (permalink / raw)


On Tue, 8 Dec 2015 13:55:31 -0800 (PST), Serge Robyns wrote:

> On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov  wrote:
>> On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote:
>> 
>> If you really need mix-in then better do it like this:
>> 
>>    type T_Abstract_Data_Store
>>       (  Clients : not null access T_Abstract_Client'Class;
>>          Store2 : not null access T_Abstract_Store2'Class
>>       )  is abstract tagged limited record
>> 
>> This would add a bit more safety, much needed when using access types.
> 
> Was considering this option, I haven't used this construct yet.  This is a
> place where reading the ALRM just gives headaches without a clue of the
> use cases.

Discriminant is a kind of read-only component with additional checks
ensuring it defined when the object is created.

From the SW POV discriminant is more an interface thing, while components
are pure implementation. When you declare something with an access
discriminant you announce that the client must take care of handling the
object the discriminant refers. You basically put the burden on the
client's shoulders, but also hint the compiler that it must check things
like that the target object don't leave the scope prematurely.

> And guess what, I just have been hit by a forgotten assignment

Right, this is a safety check. Limited types have no assignment.
Considering components or discriminants access types you should define the
semantics of assignment. Is it shallow copy or deep copy? That normally
would rule out anonymous access types.

Communication objects rarely need to be copyable. If they have to, the best
way is to add a handles layer to the reference-counted limited objects and
copy the handles. Interfaces is a help here. You can make the handle and
the target object to implement the same interface. The handle's
implementation would delegate to the target's implementation.

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

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 21:08             ` Dmitry A. Kazakov
  2015-12-08 21:55               ` Serge Robyns
@ 2015-12-09 10:53               ` G.B.
  2015-12-09 12:03                 ` Dmitry A. Kazakov
  1 sibling, 1 reply; 34+ messages in thread
From: G.B. @ 2015-12-09 10:53 UTC (permalink / raw)


On 08.12.15 22:08, Dmitry A. Kazakov wrote:

> There are people in c.l.a who believe in procedural decomposition and
> inference from the type structure. It is incompatible with fundamental
> Ada's principles of strong typing and separation of interface and
> implementation. But that is my opinion.

There are those in c.l.ada, including Dmitry Kazakov ;-) ;-) ;-),
who suggested that the parameter profile of a subprogram can
be seen as a type.  For practical matters in single programs,
this looks perfectly fine to me. Considering, if everything in
a program is a reusable building block, when is there any use?

(Moreover, what is the type of constructs that do actually reuse
if everything became an Ada type?)

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 10:53               ` G.B.
@ 2015-12-09 12:03                 ` Dmitry A. Kazakov
  2015-12-09 13:42                   ` G.B.
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-09 12:03 UTC (permalink / raw)


On Wed, 9 Dec 2015 11:53:43 +0100, G.B. wrote:

> On 08.12.15 22:08, Dmitry A. Kazakov wrote:
> 
>> There are people in c.l.a who believe in procedural decomposition and
>> inference from the type structure. It is incompatible with fundamental
>> Ada's principles of strong typing and separation of interface and
>> implementation. But that is my opinion.
> 
> There are those in c.l.ada, including Dmitry Kazakov ;-) ;-) ;-),
> who suggested that the parameter profile of a subprogram can
> be seen as a type.

You mean procedural types? Ada does not have them, only the
access-to-subprogram kludge.

> For practical matters in single programs,
> this looks perfectly fine to me. Considering, if everything in
> a program is a reusable building block, when is there any use?

What about the process of fitting? In order to reuse you need an interface
how and where does it fit. You cannot do that without types, e.g. of the
parameters. The second question is the context where you may to reuse the
block. How do you describe the context? In terms of types, the context is
everywhere the types of the interface exist (this includes subtype
constraints). That makes your "block" a mere primitive operation of those
types.

The bottom line, you can have your lazy blocks only when not reusable and
weakly typed.

> (Moreover, what is the type of constructs that do actually reuse
> if everything became an Ada type?)

I didn't say that everything is a type. I only said that procedural
decomposition is superseded by OO decomposition. Largely because
programming in terms of singular values is superseded by programming in
terms of sets of values (type) and sets of types (generic programming).
This is a normal evolutionary process to handle increasing SW complexity.

This is orthogonal to other language constructs, though most of them do
rely on types anyway, e.g. if, for, case statements require certain class
of types to work. As such they could be viewed as syntax sugar for some
primitive operations.

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


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 23:04               ` Serge Robyns
  2015-12-08 23:42                 ` Jeffrey R. Carter
@ 2015-12-09 13:40                 ` Jere
  2015-12-09 13:48                   ` G.B.
  2015-12-13 21:37                 ` Shark8
  2 siblings, 1 reply; 34+ messages in thread
From: Jere @ 2015-12-09 13:40 UTC (permalink / raw)


On Tuesday, December 8, 2015 at 6:04:08 PM UTC-5, Serge Robyns wrote:
> On Tuesday, 8 December 2015 23:55:52 UTC+1, Jeffrey R. Carter  wrote:
> > If you don't need assignment because there will never be more than one of them,
> > that's an indication that you don't need a type and a variable; you need a
> > module. I suspect you think in terms of types because C++ only provides
> > encapsulation and information hiding through its class construct. In Ada, your
> > 1st thought when you want encapsulation or information hiding should be a pkg.
> > 
> 
> How do I then pass the context?  I mean, now I pass the parameter around.  through global variables?  Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session.  I do believe these are good reasons for having a variable.

If will be having multiple ones with their own contexts then you will have to use objects from types to do that.  However, if you are only going to have one, then as Jeffrey Carter suggested, just use a package.  A useless uncompiled example (I don't have GNAT in front of me here):

package My_Package is

   type Data_Type is record
      Value1 : Integer := Integer'First;
      Value2 : Positive := Positive'First;
   end record;

   procedure Update_My_Data(Data : in Data_Type);
   function  View_My_Data return Data_Type;

end My_Package;

   
package body My_Package is

   My_Data : Data_Type;

   procedure Update_My_Data(Data : in Data_Type) is
   begin
      My_Data := Data;
   end Update_My_Data;

   function  View_My_Data return Data_Type is
   begin
      return My_Data;
   end View_My_Data;

end My_Package;

My_Data is only accessible via the package body, so any clients have to use the provided functions of the package to manipulate or look at the data.  This example is over-simplistic, but I just wanted to show you the layout.  It's not the same as using a global since the clients can't access the variable directly and you have the freedom to have the public interface hide some implementation details on how the data is actually handled.

Since you are familiar with C++, it has a similar construct (I use it for peripherals in small embedded systems):  A class with only static members and static methods.  In this case, you are not using the class to generate objects but to organize methods and data with the ability to make some of them private (which you cannot do in a namespace) while also being able to retain a private non-global static state for one thing.  It is not as elegant as an Ada package and there are still some differences, but it is the closest analog to this that C++ has.

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 12:03                 ` Dmitry A. Kazakov
@ 2015-12-09 13:42                   ` G.B.
  2015-12-09 14:23                     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: G.B. @ 2015-12-09 13:42 UTC (permalink / raw)


On 09.12.15 13:03, Dmitry A. Kazakov wrote:
> You mean procedural types?

Not exactly procedural types. I'd like a reflection of a more
clear cut distinction between when a(n MI-)type is needed for
(M)dispatching versus when the (MI-)type is needed for materializing
actual objects. Maybe that simplifies things. Example below.

> The bottom line, you can have your lazy blocks only when not reusable and
> weakly typed.

Put differently, at some point I can use "weakly" typed blocks
for getting a job done that warrants just the necessary effort.
(Yeah, I guess it's not the dream of an engineer who is looking
for some kind of "just technical perfection".)

For some perspective, dispatching can look a lot like a specially
crafted procedural thing. I think that there are cases related
to MD (as in the example of storing from Objects'Class into
Databases'Class) when declaring a regular named (MI-)type would
be going over the top.

procedure Demo is

    package Root is
       type T is abstract tagged private;
       procedure Op
         (X : in out T; Val : Natural) is abstract;
    private
       type T is abstract tagged record
          Identity : Natural; -- := ...;
       end record;
    end Root;

    package Twigs is
       type A is new Root.T with record null; end record;
       overriding procedure Op (X : in out A; Val : Natural);

       type B is new Root.T with record null; end record;
       overriding procedure Op (X : in out B; Val : Natural);
    end Twigs;
    package body Twigs is separate;

    package X is

       procedure D12
         (Left  : in out Root.T'Class;
          Right : in out Twigs.B'Class);

       procedure D22
         (Left  : in out Twigs.A'Class;
          Right : in out Twigs.B'Class);

    end X;
    package body X is separate;

    Va : Twigs.A;
    Vb : Twigs.B;

begin
    --  not Ada
    do X.D12 with X.D2'Type (Left => V1, Right => Vb);
    do X.D22 with X.D2'Type (Left => Va, Right => Vb);
    do X.D22 with X.D2'Type (Left => Va, Right => Va);  -- no

end Demo;

The last lines are not really needed, but they show where
there is a type lurking.

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 13:40                 ` Jere
@ 2015-12-09 13:48                   ` G.B.
  2015-12-09 15:33                     ` Jere
  0 siblings, 1 reply; 34+ messages in thread
From: G.B. @ 2015-12-09 13:48 UTC (permalink / raw)


On 09.12.15 14:40, Jere wrote:
> A class with only static members and static methods.

A file will do, in both C++ and in C.
Ada does not have file scope, but does
have packages, so the names are full names.

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 13:42                   ` G.B.
@ 2015-12-09 14:23                     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2015-12-09 14:23 UTC (permalink / raw)


On Wed, 9 Dec 2015 14:42:24 +0100, G.B. wrote:

> On 09.12.15 13:03, Dmitry A. Kazakov wrote:
>> You mean procedural types?
> 
> Not exactly procedural types. I'd like a reflection of a more
> clear cut distinction between when a(n MI-)type is needed for
> (M)dispatching versus when the (MI-)type is needed for materializing
> actual objects.

I think the problem is that you consider dispatching an interface-thing.
It is not. Dispatching is strictly an implementation.

It is important to understand that both primitive and class-wide operations
are defined on the whole class. It is the implementations of these that are
different. In particular a primitive operation has a body for each member
of the class. Dispatching is merely a mechanism of body selection. There
could be other mechanisms of body composition. For example body extension
as used in constructors, destructors, aggregates, assignment, stream I/O
operations.

>> The bottom line, you can have your lazy blocks only when not reusable and
>> weakly typed.
> 
> Put differently, at some point I can use "weakly" typed blocks
> for getting a job done that warrants just the necessary effort.

Yes, you can program C in Ada. The question is what for?

> For some perspective, dispatching can look a lot like a specially
> crafted procedural thing. I think that there are cases related
> to MD (as in the example of storing from Objects'Class into
> Databases'Class) when declaring a regular named (MI-)type would
> be going over the top.
> 
> procedure Demo is
> 
>     package Root is
>        type T is abstract tagged private;
>        procedure Op
>          (X : in out T; Val : Natural) is abstract;
>     private
>        type T is abstract tagged record
>           Identity : Natural; -- := ...;
>        end record;
>     end Root;
> 
>     package Twigs is
>        type A is new Root.T with record null; end record;
>        overriding procedure Op (X : in out A; Val : Natural);
> 
>        type B is new Root.T with record null; end record;
>        overriding procedure Op (X : in out B; Val : Natural);
>     end Twigs;
>     package body Twigs is separate;
> 
>     package X is
> 
>        procedure D12
>          (Left  : in out Root.T'Class;
>           Right : in out Twigs.B'Class);
> 
>        procedure D22
>          (Left  : in out Twigs.A'Class;
>           Right : in out Twigs.B'Class);
> 
>     end X;
>     package body X is separate;
> 
>     Va : Twigs.A;
>     Vb : Twigs.B;
> 
> begin
>     --  not Ada
>     do X.D12 with X.D2'Type (Left => V1, Right => Vb);
>     do X.D22 with X.D2'Type (Left => Va, Right => Vb);
>     do X.D22 with X.D2'Type (Left => Va, Right => Va);  -- no
> 
> end Demo;
> 
> The last lines are not really needed, but they show where
> there is a type lurking.

I don't understand the purpose of this example.

MI and MD are required for consistency. Practical use or not is irrelevant,
because consistency trumps everything else.

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

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 13:48                   ` G.B.
@ 2015-12-09 15:33                     ` Jere
  0 siblings, 0 replies; 34+ messages in thread
From: Jere @ 2015-12-09 15:33 UTC (permalink / raw)


On Wednesday, December 9, 2015 at 8:48:02 AM UTC-5, G.B. wrote:
> On 09.12.15 14:40, Jere wrote:
> > A class with only static members and static methods.
> 
> A file will do, in both C++ and in C.
> Ada does not have file scope, but does
> have packages, so the names are full names.

In C that's really all one can do.  In C++ one prefers not to do it using file scope as that just pollutes the global namespace.  The preference is always to use either a namespace or class to wrap members and data.  Also, one cannot get protected (c++ meaning) static methods or members using just file scope.  While that is somewhat limited in use, it does have its benefits in some designs.  The big reason is not to put all of one's stuff into the global namespace.  Then it ends up forcing one to leave behind some meaningful names in order to not clash with another item of the same signature.

Regardless, the Ada package way is much cleaner and nicer.

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 22:55                   ` Jeffrey R. Carter
@ 2015-12-09 23:03                     ` Randy Brukardt
  2015-12-10  8:38                       ` Serge Robyns
  0 siblings, 1 reply; 34+ messages in thread
From: Randy Brukardt @ 2015-12-09 23:03 UTC (permalink / raw)


"Jeffrey R. Carter" <spam.jrcarter.not@spam.not.acm.org> wrote in message 
news:n47n08$3tr$1@dont-email.me...
> On 12/08/2015 03:43 PM, Serge Robyns wrote:
>>
>> Now I've a generic question with regards to access type vs. in-out for 
>> parameters.  What is the proper Ada way to pass the "Store" around?
>> Is it:
>> Procedure Do_Something (A_Store : in out T_Abstract_Store'Class);
>> or:
>> Procedure Do_Something (A_Store : not null access 
>> T_Abstract_Store'Class);
>> ?
>>
>> As a former C programmer my habit goes for the not null access variant.
>
> As an Ada S/W engineer, you should use "in out". With "in out", the 
> compiler
> does the work of passing the object in such a way that the operation can 
> read
> and modify the value. With an access, you (or whoever calls your 
> operation) has
> to do extra work to have an access value to pass. It's unnecessary extra 
> work
> and an extra chance to get something wrong, and so to be avoided.

On top of which, it tends to limit how the client can use your package. In 
particular, the client most likely couldn't put values of your object type 
into a container, because Ada puts very strict limits on getting an access 
value into a container. (Additionally, because of the extra runtime 
accessibility checking, the access version is slower. That effect is small 
enough that it shouldn't be a major determining factor in deciding which to 
use, but if all other things are equal, why would anyone want to use the 
slower version??)

                                         Randy.


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-09 23:03                     ` Randy Brukardt
@ 2015-12-10  8:38                       ` Serge Robyns
  2015-12-10 23:43                         ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Serge Robyns @ 2015-12-10  8:38 UTC (permalink / raw)


On Thursday, 10 December 2015 00:03:15 UTC+1, Randy Brukardt  wrote:

> On top of which, it tends to limit how the client can use your package. In 
> particular, the client most likely couldn't put values of your object type 
> into a container, because Ada puts very strict limits on getting an access 
> value into a container. (Additionally, because of the extra runtime 
> accessibility checking, the access version is slower. That effect is small 
> enough that it shouldn't be a major determining factor in deciding which to 
> use, but if all other things are equal, why would anyone want to use the 
> slower version??)
> 
>                                          Randy.

This is an interesting topic.  I've always been wondering when the compiler is generating checks and when it does know he can skip them.  And what kind of test is the compiler doing on an access value.  Especially coming from the C world this is very "counter intuitive" :-P.

If the variable is as following:
  A_Reference : access not null T_something;  -- whereas T_Something is just a plain record type, i.e. not a tagged one.
and the procedure signature
  procedure Do_Something (Reference : not null access T_Something; ....);
What would happen with:
  Do_Something (A_Reference, ...);

My assumption is that the not null checks can be skipped during the call and inside the procedure.  What other checks are performed inside Do_Something for A_Reference?

Regards,
Serge

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-10  8:38                       ` Serge Robyns
@ 2015-12-10 23:43                         ` Randy Brukardt
  0 siblings, 0 replies; 34+ messages in thread
From: Randy Brukardt @ 2015-12-10 23:43 UTC (permalink / raw)


"Serge Robyns" <serge.robyns@gmail.com> wrote in message 
news:c7533283-04c4-49f6-abe8-4f47d70b86df@googlegroups.com...
On Thursday, 10 December 2015 00:03:15 UTC+1, Randy Brukardt  wrote:
...
>If the variable is as following:
 > A_Reference : access not null T_something;  -- whereas T_Something is 
just a plain record type, i.e. not a tagged one.

If this is a stand-alone variable, it has dynamic accessibility (that is, 
the accessibility is that of the designated object, wherever it is).

>and the procedure signature
>  procedure Do_Something (Reference : not null access T_Something; ....);
>What would happen with:
 > Do_Something (A_Reference, ...);

Anonymous access parameters always have dynamic accessibility.

>My assumption is that the not null checks can be skipped during the call 
>and inside the procedure.

Most likely. Janus/Ada has an "assume nothing" mode as one could use 
streaming or Unchecked_Conversion to put a null value into a not null 
component, and in that case a check would be made anything. But that's 
usually only used for debugging when all else has failed (and it usually 
turns out that compiler assumptions aren't the problem).

> What other checks are performed inside Do_Something for A_Reference?

Accessibility checks. As I noted, the accessibility of Do_Something is 
dynamic. (I've always thought "accessibility" was a terrible term -- the 
correct concept is "lifetime". The designated object has to live long enough 
for the use it's put to, and if not, an accessibility check fails.)

Even if the body of Do_Something doesn't contain any accessibility checks 
(today), it could easily be changed to have one, so it's necessary to pass 
that information along with the pointer. And that can't be optimized away 
because to do so would make a body dependence (i.e., recompiling the body 
would require recompiling all of the calls).

To be fair, it would be possible to optimize the overhead away if the 
subprogram is inlined, or if the subprogram has a precondition that requires 
the object to be library-level. But I don't know if any compilers actually 
do any such optimizations.

Thus, "in out T" (which has static accessibility and thus doesn't have any 
accessibility overhead) is almost always cheaper than "not null access T" 
which has dynamic accessibility (and thus has to pass an accessibility 
value). As I said yesterday, the effect is small, but it's clearly non-zero 
(passing two words costs more than passing one word).

                                      Randy.



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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-08 23:04               ` Serge Robyns
  2015-12-08 23:42                 ` Jeffrey R. Carter
  2015-12-09 13:40                 ` Jere
@ 2015-12-13 21:37                 ` Shark8
  2015-12-14  2:20                   ` Jeffrey R. Carter
  2 siblings, 1 reply; 34+ messages in thread
From: Shark8 @ 2015-12-13 21:37 UTC (permalink / raw)


On Tuesday, December 8, 2015 at 4:04:08 PM UTC-7, Serge Robyns wrote:
> 
> How do I then pass the context?  I mean, now I pass the parameter around.  through global variables?

No, you use the package.
If you want a single application-wide object you can use a package. Here is a single counter:


Package Counter is
  Procedure Inc;
  Procedure Dec;
  Function Value Return Natural;
Private
  Data : Natural := 0;
End Counter;

Package Body Counter is
  Procedure Inc is
  begin
   Data:= Natural'Succ( Data );
  end Inc;

  Procedure Dec is
  begin
   Data:= Natural'Pred( Data );
  end Dec;

  Function Value return Natural is
  ( Data );
End Counter;

You could also enclose that sort of logic in a task or protected object; and if you need multiple singletons you could always use a generic version and then make an instance for each needed singleton.

What those above are warning you against is trying to write C (or C++) in Ada. There's a lot of differences that can usually greatly simplify and/or make your code more reliable. (The most obvious of those is probably the Array type, which because it contains the range/indices means you don't have to pass that sort of info in parameters.

> Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session.  I do believe these are good reasons for having a variable.

That is doable, I did it with a mock-up test and it worked pretty nicely... though personally I have had problems getting a good DB interface/binding up and running w/ Ada.


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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-13 21:37                 ` Shark8
@ 2015-12-14  2:20                   ` Jeffrey R. Carter
  2015-12-15  7:26                     ` Shark8
  0 siblings, 1 reply; 34+ messages in thread
From: Jeffrey R. Carter @ 2015-12-14  2:20 UTC (permalink / raw)


On 12/13/2015 02:37 PM, Shark8 wrote:
> 
> Package Counter is
>   Procedure Inc;
>   Procedure Dec;
>   Function Value Return Natural;
> Private
>   Data : Natural := 0;
> End Counter;

I wouldn't put the state in the private part.

> Package Body Counter is

Instead, I'd put it in the body

   Data : Natural := 0;

>   Procedure Inc is
>   begin
>    Data:= Natural'Succ( Data );
>   end Inc;
> 
>   Procedure Dec is
>   begin
>    Data:= Natural'Pred( Data );
>   end Dec;

I'd also use "+ 1" or "- 1", since those are clearer.

>   Function Value return Natural is
>   ( Data );
> End Counter;

-- 
Jeff Carter
"I don't know why I ever come in here. The
flies get the best of everything."
Never Give a Sucker an Even Break
102

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

* Re: I'm facing an issue with: call to abstract procedure must be dispatching
  2015-12-14  2:20                   ` Jeffrey R. Carter
@ 2015-12-15  7:26                     ` Shark8
  0 siblings, 0 replies; 34+ messages in thread
From: Shark8 @ 2015-12-15  7:26 UTC (permalink / raw)


On Sunday, December 13, 2015 at 7:20:40 PM UTC-7, Jeffrey R. Carter wrote:
> 
> I wouldn't put the state in the private part.
> Instead, I'd put it in the body

I tend to go with body, though there are some instances where you need to put it in private. (Namely: when child units need to access the internal representation of the data.)

> I'd also use "+ 1" or "- 1", since those are clearer.

The reason I used Succ and Pred was that I was originally going to [also] include a generic version, which would need to replace Natural w/ the formal type parameter:

Generic
  Type Data_Type is (<>);
  Default : in Data_Type;
Package Generic_Counter is
  Procedure Inc;
  Procedure Dec;
  Function Value Return Data_Type;
End Generic_Counter;

Package Body Generic_Counter is
  Data : Data_Type := Default;
  Procedure Inc is
  begin
   Data:= Data_Type'Succ( Data );
  end Inc;

  Procedure Dec is
  begin
   Data:= Data_Type'Pred( Data );
  end Dec;

  Function Value return Data_Type is
  ( Data );
End Generic_Counter;

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

end of thread, other threads:[~2015-12-15  7:26 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns
2015-12-08 16:59 ` G.B.
2015-12-08 17:04 ` Dmitry A. Kazakov
2015-12-08 17:24   ` Serge Robyns
2015-12-08 17:42     ` Dmitry A. Kazakov
2015-12-08 18:00       ` Serge Robyns
2015-12-08 18:22         ` Dmitry A. Kazakov
2015-12-08 20:21           ` Serge Robyns
2015-12-08 21:08             ` Dmitry A. Kazakov
2015-12-08 21:55               ` Serge Robyns
2015-12-08 22:43                 ` Serge Robyns
2015-12-08 22:55                   ` Jeffrey R. Carter
2015-12-09 23:03                     ` Randy Brukardt
2015-12-10  8:38                       ` Serge Robyns
2015-12-10 23:43                         ` Randy Brukardt
2015-12-09  8:48                 ` Dmitry A. Kazakov
2015-12-09 10:53               ` G.B.
2015-12-09 12:03                 ` Dmitry A. Kazakov
2015-12-09 13:42                   ` G.B.
2015-12-09 14:23                     ` Dmitry A. Kazakov
2015-12-08 22:55             ` Jeffrey R. Carter
2015-12-08 23:04               ` Serge Robyns
2015-12-08 23:42                 ` Jeffrey R. Carter
2015-12-09 13:40                 ` Jere
2015-12-09 13:48                   ` G.B.
2015-12-09 15:33                     ` Jere
2015-12-13 21:37                 ` Shark8
2015-12-14  2:20                   ` Jeffrey R. Carter
2015-12-15  7:26                     ` Shark8
2015-12-08 17:30 ` Jeffrey R. Carter
2015-12-08 17:48   ` Serge Robyns
2015-12-08 18:46     ` Jeffrey R. Carter
2015-12-08 20:28       ` Serge Robyns
2015-12-08 22:20     ` Simon Wright

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