comp.lang.ada
 help / color / mirror / Atom feed
* Access types as parameters
@ 2009-07-17  8:39 Rick
  2009-07-17 15:03 ` Adam Beneschan
  0 siblings, 1 reply; 31+ messages in thread
From: Rick @ 2009-07-17  8:39 UTC (permalink / raw)


Given:

   type My_Type is  ...;
and
   type My_Access_Type is access all My_Type'Class;

what is the practical difference between:

   function My_Function (Thing : access My_Type'Class) return
Positive;
and
   function My_Function (Thing : My Access_Type return Positive;



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

* Re: Access types as parameters
  2009-07-17  8:39 Access types as parameters Rick
@ 2009-07-17 15:03 ` Adam Beneschan
  2009-07-17 16:28   ` Hibou57 (Yannick Duchêne)
  0 siblings, 1 reply; 31+ messages in thread
From: Adam Beneschan @ 2009-07-17 15:03 UTC (permalink / raw)


On Jul 17, 1:39 am, Rick <rickdu...@gmail.com> wrote:
> Given:
>
>    type My_Type is  ...;
> and
>    type My_Access_Type is access all My_Type'Class;
>
> what is the practical difference between:
>
>    function My_Function (Thing : access My_Type'Class) return
> Positive;
> and
>    function My_Function (Thing : My Access_Type return Positive;

The accessibility level rules say that a value of type My_Access_Type
cannot point to an object that might go away before My_Access_Type
does.  (Unless you use 'Unchecked_Access to create the access value.)
This is to prevent dangling references.  The rules apply to parameters
also, so if you call the second My_Function, you can't give it the
'Access of an object that is deeper than My_Access_Type.  If
My_Access_Type is declared at library level (i.e. in a library
package, not inside a procedure or function), then you can't say

   procedure Some_Procedure is
        X : aliased My_Type;  --or some type derived from My_Type
   begin
        My_Function (X'access);  -- i.e. the second My_Function

But this restriction doesn't apply to the first My_Function.  Since
that My_Function has an access parameter, you can pass the 'Access of
any object of the right type, no matter how deeply nested inside
procedures the object is.

                                    -- Adam



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

* Re: Access types as parameters
  2009-07-17 15:03 ` Adam Beneschan
@ 2009-07-17 16:28   ` Hibou57 (Yannick Duchêne)
  2009-07-17 23:25     ` rickduley
  0 siblings, 1 reply; 31+ messages in thread
From: Hibou57 (Yannick Duchêne) @ 2009-07-17 16:28 UTC (permalink / raw)


Adam has already given a good answer, but I may say a bit the same in
shorter words :

access My_Type'Class in a function declaration, is a so called “
anonymous type ”, and as this type is only declared locally, it cannot
match any other types defined at wider level (beceause this would
simply not be type conformant).

access My_Type'Class does not have a so wide validity as
My_Access_Type may have.

This is conveniant and safe

More words....

As an hint : you may use My_Access_Type if the reference is to be
stored into a record (as an exemple) and may use access My_Type'Class
if you only need it locally. Looking at the function signature, you
may then quicly see what kind of things the function may allowed it-
self to do with the reference you gave it.

If you see the function wants an “ access My_Type'Class ”, you nearly
do not have to bother about anything, but if you see it wants a “
My_Access_Type ” you may need to be sure there is a good collaboration
between the caller and the callee. This is anyway, a good invitation
to read the function documentation (if comments are provided about its
usage).

But this does not means “ access My_Type'Class ” is better beceause it
is “ safer ” (quotes, beceause the other way is not always not safe),
as sometime, My_Access_Type is mandatory, depending on what the
function have to do with the reference.



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

* Re: Access types as parameters
  2009-07-17 16:28   ` Hibou57 (Yannick Duchêne)
@ 2009-07-17 23:25     ` rickduley
  2009-07-18  1:03       ` Randy Brukardt
  0 siblings, 1 reply; 31+ messages in thread
From: rickduley @ 2009-07-17 23:25 UTC (permalink / raw)
  Cc: rickduley

On Jul 18, 12:28 am, Hibou57 (Yannick Duchêne)
<yannick_duch...@yahoo.fr> wrote:
> Adam has already given a good answer, but I may say a bit the same in
> shorter words :
>
> access My_Type'Class in a function declaration, is a so called “
> anonymous type ”, and as this type is only declared locally, it cannot
> match any other types defined at wider level (beceause this would
> simply not be type conformant).
>
> access My_Type'Class does not have a so wide validity as
> My_Access_Type may have.
>
> This is conveniant and safe
>
> More words....
>
> As an hint : you may use My_Access_Type if the reference is to be
> stored into a record (as an exemple) and may use access My_Type'Class
> if you only need it locally. Looking at the function signature, you
> may then quicly see what kind of things the function may allowed it-
> self to do with the reference you gave it.
>
> If you see the function wants an “ access My_Type'Class ”, you nearly
> do not have to bother about anything, but if you see it wants a “
> My_Access_Type ” you may need to be sure there is a good collaboration
> between the caller and the callee. This is anyway, a good invitation
> to read the function documentation (if comments are provided about its
> usage).
>
> But this does not means “ access My_Type'Class ” is better beceause it
> is “ safer ” (quotes, beceause the other way is not always not safe),
> as sometime, My_Access_Type is mandatory, depending on what the
> function have to do with the reference.

Hi Yannick

Thanks for that.

I don't quite see what you mean by:

> But this does not means “ access My_Type'Class ” is better beceause it
> is “ safer ” (quotes, beceause the other way is not always not safe),
> as sometime, My_Access_Type is mandatory, depending on what the
> function have to do with the reference.

If the function is called, and the actual parameter is valid, what
difference can it make what the function does with the data?



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

* Re: Access types as parameters
  2009-07-17 23:25     ` rickduley
@ 2009-07-18  1:03       ` Randy Brukardt
  2009-07-19 22:57         ` rickduley
  0 siblings, 1 reply; 31+ messages in thread
From: Randy Brukardt @ 2009-07-18  1:03 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1836 bytes --]

"rickduley" <rickduley@gmail.com> wrote in message 
news:8410fc60-9b8a-4f82-92fc-622a6bbe5931@i18g2000pro.googlegroups.com...
> I don't quite see what you mean by:
>
>> But this does not means � access My_Type'Class � is better beceause it
>> is � safer � (quotes, beceause the other way is not always not safe),
>> as sometime, My_Access_Type is mandatory, depending on what the
>> function have to do with the reference.
>
>If the function is called, and the actual parameter is valid, what
>difference can it make what the function does with the data?

He might be referring to the fact that the uses of the anonymous access 
parameter might raise Program_Error (as there is a dynamic accessibility 
check) while the named example is either going to be legal (and work 
properly) or illegal.

My answer to the original question would have been:

>Given:
>
>   type My_Type is  ...;
>and
>   type My_Access_Type is access all My_Type'Class;
>
>what is the practical difference between:
>
>   function My_Function (Thing : access My_Type'Class) return Positive;
>and
>   function My_Function (Thing : My Access_Type) return Positive;

The first has a runtime parameter passing overhead that the latter does not, 
and depending on how the parameter is used inside of the function, a 
significant possibility of raising Program_Error later. (Something Bob Duff 
calls a "tripping hazard"). This latter problem is especially bad as it is 
likely to be missed in unit testing (in that sense, it is similar to 
assuming the lower bound of a string is 1 - tests often make the same 
assumption).

So it is best to avoid the first form unless you have a particular need for 
dispatching on an access value (which won't happen here, because the 
designated type is class-wide).

                                             Randy.





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

* Re: Access types as parameters
  2009-07-18  1:03       ` Randy Brukardt
@ 2009-07-19 22:57         ` rickduley
  2009-07-20  0:10           ` John B. Matthews
                             ` (3 more replies)
  0 siblings, 4 replies; 31+ messages in thread
From: rickduley @ 2009-07-19 22:57 UTC (permalink / raw)


Hi Randy

You wrote:
> So it is best to avoid the first form unless you have a particular need for
> dispatching on an access value (which won't happen here, because the
> designated type is class-wide).

Why then does GtkAda consistently use the first form, i.e.:
function My_Function (Thing : access My_Type'Class) return Positive;
for an 'Initialize' function?

It actually uses the form (this for Gtk.Button.Gtk_Button):
procedure Initialize
  (Button : access Gtk_Button_Record'Class;
   Label  : UTF8_String);



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

* Re: Access types as parameters
  2009-07-19 22:57         ` rickduley
@ 2009-07-20  0:10           ` John B. Matthews
  2009-07-20  8:13           ` Dmitry A. Kazakov
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 31+ messages in thread
From: John B. Matthews @ 2009-07-20  0:10 UTC (permalink / raw)


In article 
<e325296c-7114-4540-9406-b7426da408dd@f18g2000prf.googlegroups.com>,
 rickduley <rickduley@gmail.com> wrote:

> Hi Randy
> 
> You wrote:
> > So it is best to avoid the first form unless you have a particular need for
> > dispatching on an access value (which won't happen here, because the
> > designated type is class-wide).
> 
> Why then does GtkAda consistently use the first form, i.e.:
> function My_Function (Thing : access My_Type'Class) return Positive;
> for an 'Initialize' function?

IIUC, "Thing" typically points to an object managed by the Gtk library. 
It's outside the scope of Ada's accessibility rules.

> It actually uses the form (this for Gtk.Button.Gtk_Button):
> procedure Initialize
>   (Button : access Gtk_Button_Record'Class;
>    Label  : UTF8_String);

The source mentions the section "Creating your own widgets" in the 
user's guide. The section "Creating new widgets in Ada" addresses the 
Initialize procedure explicitly:

<http://libre.adacore.com/wp-content/files/auto_update/gtkada-docs/
gtkada_ug/gtkada_ug.html>

-- 
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>



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

* Re: Access types as parameters
  2009-07-19 22:57         ` rickduley
  2009-07-20  0:10           ` John B. Matthews
@ 2009-07-20  8:13           ` Dmitry A. Kazakov
  2009-07-21  0:34           ` Randy Brukardt
  2009-07-21 14:34           ` Adam Beneschan
  3 siblings, 0 replies; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-07-20  8:13 UTC (permalink / raw)


On Sun, 19 Jul 2009 15:57:12 -0700 (PDT), rickduley wrote:

>> So it is best to avoid the first form unless you have a particular need for
>> dispatching on an access value (which won't happen here, because the
>> designated type is class-wide).
> 
> Why then does GtkAda consistently use the first form,

alas!

> i.e.:
> function My_Function (Thing : access My_Type'Class) return Positive;
> for an 'Initialize' function?

You mean 

   function My_Function (Thing : access Gtk_XXX_Record'Class)
      return Positive;

I think it should better be

   function My_Function (Thing : Gtk_XXX) return Positive;

since GtkAda has Gtk_XXX declared as access to every Gtk_XXX_Record'Class.

> It actually uses the form (this for Gtk.Button.Gtk_Button):
> procedure Initialize
>   (Button : access Gtk_Button_Record'Class;
>    Label  : UTF8_String);

Well, this is likely a design bug. Gtk_Object_Record should have been
limited controlled with Initialize inherited from
Ada.Finalization.Limited_Controlled.

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



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

* Re: Access types as parameters
  2009-07-19 22:57         ` rickduley
  2009-07-20  0:10           ` John B. Matthews
  2009-07-20  8:13           ` Dmitry A. Kazakov
@ 2009-07-21  0:34           ` Randy Brukardt
  2009-07-21 14:34           ` Adam Beneschan
  3 siblings, 0 replies; 31+ messages in thread
From: Randy Brukardt @ 2009-07-21  0:34 UTC (permalink / raw)


"rickduley" <rickduley@gmail.com> wrote in message 
news:e325296c-7114-4540-9406-b7426da408dd@f18g2000prf.googlegroups.com...
> Hi Randy
>
> You wrote:
>> So it is best to avoid the first form unless you have a particular need 
>> for
>> dispatching on an access value (which won't happen here, because the
>> designated type is class-wide).
>
> Why then does GtkAda consistently use the first form, i.e.:
> function My_Function (Thing : access My_Type'Class) return Positive;
> for an 'Initialize' function?
>
> It actually uses the form (this for Gtk.Button.Gtk_Button):
> procedure Initialize
>  (Button : access Gtk_Button_Record'Class;
>   Label  : UTF8_String);

You'd have to ask the designers of GTKAda that question. I think it's 
terrible to define Ada-level interfaces that way, but they may have had good 
reasons. See Claw 
(http://www.rrsoftware.com/html/prodinf/claw/clawintro.html for the free 
version) to see how I think a real-world Ada GUI library ought to be 
designed. (Well, more accurately, how I thought 10 years ago, I'd make some 
minor changes today, mainly making most of the types limited, as that works 
much better in Ada nowadays).

                                     Randy.


                                     Randy. 





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

* Re: Access types as parameters
  2009-07-19 22:57         ` rickduley
                             ` (2 preceding siblings ...)
  2009-07-21  0:34           ` Randy Brukardt
@ 2009-07-21 14:34           ` Adam Beneschan
  2009-07-23  2:11             ` Stephen Leake
  3 siblings, 1 reply; 31+ messages in thread
From: Adam Beneschan @ 2009-07-21 14:34 UTC (permalink / raw)


On Jul 19, 3:57 pm, rickduley <rickdu...@gmail.com> wrote:
> Hi Randy
>
> You wrote:
> > So it is best to avoid the first form unless you have a particular need for
> > dispatching on an access value (which won't happen here, because the
> > designated type is class-wide).
>
> Why then does GtkAda consistently use the first form, i.e.:
> function My_Function (Thing : access My_Type'Class) return Positive;
> for an 'Initialize' function?
>
> It actually uses the form (this for Gtk.Button.Gtk_Button):
> procedure Initialize
>   (Button : access Gtk_Button_Record'Class;
>    Label  : UTF8_String);

Well, for a *function*, using an access parameter can help get around
the rule that you can't have an IN OUT parameter to a function (a rule
that I think is going away in the next revision of the language).  By
making it an anonymous access type, rather than a named access type,
that lets you pass local variables that the function can modify.

For a procedure, there's less reason to do so.  I don't know anything
about GTK, so I don't know why this wouldn't have worked, if all
Initialize is doing is to set up some fields in Button:

procedure Initialize
              (Button : in out Gtk_Button_Record'Class;
               Label  : UTF8_String);

If, on the other hand, Initialize needs Button as an access so that it
can store it in a data structure, then it probably would have been
best to make it a named access type, to ensure that dangling
references aren't stored in that data structure.

If Initialize is an imported routine from a C library---well, in that
case, I guess you do whatever works.  But I'd still think that using
"in out" would have the same effect---the address of the record is
passed.  (In fact, for an anonymous access parameter, the code
normally CANNOT pass *just* an address; there has to be additional
information in case the procedure needs to do an accessibility level
check.  But for a procedure imported from C, the parameter passing
mechanisms may be different.)

Randy is right, and I thought about saying something along those lines
in my first response but decided (maybe unwisely) not to complicate
things.  You don't need to pass an anonymous access parameter if all
you're going to do is modify the accessed object---IN OUT will do.
And if you're going to store the access parameter in a global
structure, you'll get a runtime error if you try to store a dangling
reference, so it's best to use a global access type so that
accessibility level errors are caught at compile time.  There may be
some esoteric situations, other than dispatching, where passing an
anonymous access parameter may be useful: perhaps the procedure can
determine at runtime whether to store an access value or make a copy
of the object depending on the accessibility level, or perhaps the
procedure wants the ability to accept NULL to indicate that there is
no object to modify, or perhaps a procedure takes two access
parameters and sets up the two accessed objects to point to each
other.  But those all seem very unusual, and may be due to an inferior
design anyway.

                                         -- Adam








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

* Re: Access types as parameters
  2009-07-21 14:34           ` Adam Beneschan
@ 2009-07-23  2:11             ` Stephen Leake
  2009-08-11 23:41               ` Randy Brukardt
  0 siblings, 1 reply; 31+ messages in thread
From: Stephen Leake @ 2009-07-23  2:11 UTC (permalink / raw)


Adam Beneschan <adam@irvine.com> writes:

> For a procedure, there's less reason to do so.  I don't know anything
> about GTK, so I don't know why this wouldn't have worked, if all
> Initialize is doing is to set up some fields in Button:
>
> procedure Initialize
>               (Button : in out Gtk_Button_Record'Class;
>                Label  : UTF8_String);
>
> If, on the other hand, Initialize needs Button as an access so that it
> can store it in a data structure, then it probably would have been
> best to make it a named access type, to ensure that dangling
> references aren't stored in that data structure.

Most GTK subprograms do need access values; the same is true of any
system that deals with derived types at multiple levels. The only
library-level object that can hold any type in the hierarchy is a
class-wide pointer. Lists of objects must hold pointers, etc.

So one reason to use 'access' instead of 'in out' is simply to avoid
the user having to type '.all' everywhere.

A reason to use 'access Record_Type' instead of 'in Access_Type' is to
avoid explicit type conversions. Given:

package Widget is
    type Gtk_Widget_Record is ...;
    type Gtk_Widget is access all Gtk_Widget_Record'class;

    procedure Show (Widget : in Gtk_Widget);
end Widget;

package Window is
    type Gtk_Window_Record is new Gtk_Widget_Record with ...;
    type Gtk_Window is access all Gtk_Window_Record'class;
end Window;

    Window : Gtk_Window := ...;

then this does not work:

   Widget.Show (Window);

but this does:

   Widget.Show (Gtk_Widget (Window));

This is just annoying!

However, if Show is declared:

procedure Show (Widget : access constant Gtk_Widget_Record'class);

Then Show matches any access type in the class hierarchy; 

    Widget.Show (Window); 

works. 

In addition, leaving out the 'class makes Show a primitive operation,
which has many advantages.

I've been struggling with this issue in OpenToken, and settled on
using 'access Record_Type' as the best compromise. Now I just need to add
'constant' in all the right places; that wasn't allowed in Ada 95,
when OpenToken was first written.
    
In some cases, I have both class-wide and primitive operations:

   type Instance is abstract tagged private;
   subtype Class is Instance'Class;
   type Handle is access all Class;

   function Name (Token : in Instance) return String is abstract;

   --  Dispatching calls to Name
   function Name_Dispatch (Token : in Class) return String;
   function Name_Dispatch (Token : access constant Instance'Class) return String;

That gives the best of all cases, at the expense of writting more
code.

> And if you're going to store the access parameter in a global
> structure, you'll get a runtime error if you try to store a dangling
> reference, so it's best to use a global access type so that
> accessibility level errors are caught at compile time.  

This is a problem; people keep stumbling across it. Still, you get
used to it after a while, and stop trying to pass 'access of a local
variable.

-- 
-- Stephe



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

* Re: Access types as parameters
  2009-07-23  2:11             ` Stephen Leake
@ 2009-08-11 23:41               ` Randy Brukardt
  2009-08-12  2:22                 ` Stephen Leake
  0 siblings, 1 reply; 31+ messages in thread
From: Randy Brukardt @ 2009-08-11 23:41 UTC (permalink / raw)


I know this is old, but it is so misguided that it needs a 
reply...especially as it is from someone who usually knowledgable on this 
forum. - RLB

"Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message 
news:u63dkb12m.fsf@stephe-leake.org...
...
> Most GTK subprograms do need access values; the same is true of any
> system that deals with derived types at multiple levels. The only
> library-level object that can hold any type in the hierarchy is a
> class-wide pointer. Lists of objects must hold pointers, etc.

All of the indefinite containers can hold class-wide objects. No (explicit) 
pointers are needed.

One of the goals for the next revision of Ada is that the vast majority of 
reasonable data structures can be reasonably written with the 
Ada.Containers. In particular, the multiway tree container can be used to 
model almost all forms for tree structure (and many graphs as well). That 
should mean that the need for explicit access types will be greatly reduced 
when class-wide programming.

Now, of course, GTK was designed for Ada 95, and those options weren't 
available then, so misguided choices were more justifiable.

...
> A reason to use 'access Record_Type' instead of 'in Access_Type' is to
> avoid explicit type conversions.

This is a terrible reason: explicit conversions show when you are converting 
accesses from one "domain of use" to another. (I'm thinking of each named 
access type as a "domain-of-use". It only makes sense to have multiple named
access types for a particular designated type if there are different 
"domains-of-use" [such as different storage pools or disjoint data 
structures].) The type conversions make it clear when you are converting 
from one "domain-of-use" to another, something that should be rare.

Anonymous access types hide that, and make it essentially impossible to 
determine what the storage pool of an access is -- meaning that most values 
that pass through anonymous access cannot be deallocated even if converted 
to a named type (because there is no way to know if it is the *right* named 
type). [Technically, they can be deallocated, but the program is erroneous 
and may do anything.]

Finally, the implicit conversions are only allow to the anonymous type. To 
get back to a named type, you have to go back to explicit conversions, so 
you don't really save anything. In your example:

> Given:
>
> package Widget is
>    type Gtk_Widget_Record is ...;
>    type Gtk_Widget is access all Gtk_Widget_Record'class;
>
>    procedure Show (Widget : in Gtk_Widget);
> end Widget;
>
> package Window is
>    type Gtk_Window_Record is new Gtk_Widget_Record with ...;
>    type Gtk_Window is access all Gtk_Window_Record'class;
> end Window;
>
>    Window : Gtk_Window := ...;
>
> then this does not work:
>
>   Widget.Show (Window);
>
> but this does:
>
>   Widget.Show (Gtk_Widget (Window));
>
> This is just annoying!
>
> However, if Show is declared:
>
> procedure Show (Widget : access constant Gtk_Widget_Record'class);
>
> Then Show matches any access type in the class hierarchy;
>
>    Widget.Show (Window);
>
> works.

Of course, Show in this case surely should be dispatching (even if the 
interface itself doesn't use that property) so that extensions can extend 
the operation, so the declaration should be:
    procedure Show (Widget : in Gtk_Widget);
and the call should be
    Show (Window.all);

Probably the real reason the designers of GTK made this an anonymous access 
parameter is to get that dispatching property. It surely has little to do 
with implicit conversions.

[The fact that you can dispatch on access types in Ada, but only with this 
bizarre syntax is a highly misguided feature of the language IMHO. It was 
added only because of the IN OUT parameter restrictions and the insistence 
of some that that not being able to copy lousy C++ and Java designs directly 
(rather than better Ada designs not using explicit access types) would harm 
the language's use in some way.]

Anyway, the real issue that these implicit conversions don't work in many 
cases that you would expect them to. For instance, inside of Show, if you 
need to treat the parameter as a value of GTK_Widget:

    Widget_List : GTK_Widget;

    Widget_List := Widget; -- Illegal. Anon to Named conversions not 
implicit!
    Widget_List := GTK_Widget(Widget);

Indeed, the only reason (in Ada 95) why the anonymous access conversions are 
implicit is the obvious problem that they don't have a name! Else I think 
they would have in fact required a conversion.

It's possible that we'd going to fix this conversion problem in the next 
version of Ada, but we will have to do more study to ensure that we don't 
introduce a show-stopping incompatibility.

...
> I've been struggling with this issue in OpenToken, and settled on
> using 'access Record_Type' as the best compromise. Now I just need to add
> 'constant' in all the right places; that wasn't allowed in Ada 95,
> when OpenToken was first written.

That may have made sense in Ada 95, but now it is a terrible choice, as it 
forces the user to do all memory management on the objects. If you force 
passing access-to-objects, you cannot store the objects in a container and 
allow the container to do the storage management. (It also requires adding a 
huge amount of noise to stack-allocated objects -- the need to declare 
everything "aliased" and 'access on every call -- and the use of 
stack-allocated objects should always be a primary goal. Why OpenToken 
cannot be designed to usually use stack-allocated objects escapes me...)

> In some cases, I have both class-wide and primitive operations:
>
>   type Instance is abstract tagged private;
>   subtype Class is Instance'Class;
>   type Handle is access all Class;
>
>   function Name (Token : in Instance) return String is abstract;
>
>   --  Dispatching calls to Name
>   function Name_Dispatch (Token : in Class) return String;
>   function Name_Dispatch (Token : access constant Instance'Class) return 
> String;
>
> That gives the best of all cases, at the expense of writting more
> code.

Let me get this straight. You declare an extra routine with a much longer 
name and a complex (and expensive at runtime call-sequence) in order that 
you can avoid writing ".all" periodically??

That is, if you have
    type Any_Instance_Class is access all Instance'Class;
    An_Instance : Any_Instance_Class;

(and appropriate visibility),

you would rather write:
    Name_Dispatch (An_Instance);

rather than
    Name (An_Instance.all);

???

Moreover, if you are using Ada 2005, you probably should write:
   An_Instance.Name

And you don't even need the .all anymore! (Nor do you need "appropriate 
visibility"!)

Talk about a lot of work for nothing. ;-)

>> And if you're going to store the access parameter in a global
>> structure, you'll get a runtime error if you try to store a dangling
>> reference, so it's best to use a global access type so that
>> accessibility level errors are caught at compile time.
>
> This is a problem; people keep stumbling across it. Still, you get
> used to it after a while, and stop trying to pass 'access of a local
> variable.

One would hope that designers of Ada interfaces would talk more care for the 
users than to say "they get used to it after a while". That sort of ticking 
time bomb (that easily can be missed in unit testing!) is very adverse to 
the goals of safety espoused by many in this group (including me!). It has 
the potential of making Ada into just another programming language.

                                                                Randy.





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

* Re: Access types as parameters
  2009-08-11 23:41               ` Randy Brukardt
@ 2009-08-12  2:22                 ` Stephen Leake
  2009-08-13  1:06                   ` Randy Brukardt
  0 siblings, 1 reply; 31+ messages in thread
From: Stephen Leake @ 2009-08-12  2:22 UTC (permalink / raw)


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

> I know this is old, but it is so misguided that it needs a 
> reply...especially as it is from someone who usually knowledgable on this 
> forum. - RLB

Thanks :).

> "Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message 
> news:u63dkb12m.fsf@stephe-leake.org...
> ...
>> Most GTK subprograms do need access values; the same is true of any
>> system that deals with derived types at multiple levels. The only
>> library-level object that can hold any type in the hierarchy is a
>> class-wide pointer. Lists of objects must hold pointers, etc.
>
> All of the indefinite containers can hold class-wide objects. No (explicit) 
> pointers are needed.

You mean the language defined containers in Ada.Containers.*. Yes, the
pointers are not explicit, but they are needed in the bodies. That's
how the language works.

Gtk has its own container hierarchy. So it needs pointers.

> One of the goals for the next revision of Ada is that the vast majority of 
> reasonable data structures can be reasonably written with the 
> Ada.Containers. 

I agree this is a reasonable approach for many applications, but
trying to use Ada.Containers to implement the Gtk container hierarchy
would be a nightmare.

> In particular, the multiway tree container can be used to model
> almost all forms for tree structure (and many graphs as well). That
> should mean that the need for explicit access types will be greatly
> reduced when class-wide programming.

This is true, but only if starting from scratch in a new system. And
it's not always the best solution anyway; each application has
particular needs. 

>> A reason to use 'access Record_Type' instead of 'in Access_Type' is to
>> avoid explicit type conversions.
>
> This is a terrible reason: explicit conversions show when you are converting 
> accesses from one "domain of use" to another. 

Not in this case. The domain is always "some Gtk object". Or "some
OpenToken object".

> (I'm thinking of each named access type as a "domain-of-use". It
> only makes sense to have multiple named access types for a
> particular designated type if there are different "domains-of-use"
> [such as different storage pools or disjoint data structures].) 

GtkAda and OpenToken don't have different named access types for one
type; they have different named access types for types in a hierarchy.

That allows the user to have collections of "any Gtk object", and
collections of "Gtk windows" or "my dialog objects". All of these
occur in a real Gtk application, and all need their own access type.

> The type conversions make it clear when you are converting from one
> "domain-of-use" to another, something that should be rare.

"converting" from one level in the type hierarchy to another is not
rare. That's part of the point of a type hierarchy. Most of the time,
it's just a view conversion, not a "real" conversion.

To be convincing, I'd have to post an example from Gtk or OpenToken
written both ways; maybe later I'll find time for that.

> Anonymous access types hide that, and make it essentially impossible to 
> determine what the storage pool of an access is -- meaning that most values 
> that pass through anonymous access cannot be deallocated even if converted 
> to a named type (because there is no way to know if it is the *right* named 
> type). [Technically, they can be deallocated, but the program is erroneous 
> and may do anything.]

That's true. But it just forces better application design.

Deallocating should be done by the same chunk of code that allocates,
so this shouldn't be an issue.

That is how OpenToken works internally.

> Finally, the implicit conversions are only allow to the anonymous type. To 
> get back to a named type, you have to go back to explicit conversions, so 
> you don't really save anything. In your example:
>
>> Given:
>>
>> package Widget is
>>    type Gtk_Widget_Record is ...;
>>    type Gtk_Widget is access all Gtk_Widget_Record'class;
>>
>>    procedure Show (Widget : in Gtk_Widget);
>> end Widget;
>>
>> package Window is
>>    type Gtk_Window_Record is new Gtk_Widget_Record with ...;
>>    type Gtk_Window is access all Gtk_Window_Record'class;
>> end Window;
>>
>>    Window : Gtk_Window := ...;
>>
>> then this does not work:
>>
>>   Widget.Show (Window);
>>
>> but this does:
>>
>>   Widget.Show (Gtk_Widget (Window));
>>
>> This is just annoying!
>>
>> However, if Show is declared:
>>
>> procedure Show (Widget : access constant Gtk_Widget_Record'class);
>>
>> Then Show matches any access type in the class hierarchy;
>>
>>    Widget.Show (Window);
>>
>> works.
>
> Of course, Show in this case surely should be dispatching (even if the 
> interface itself doesn't use that property) so that extensions can extend 
> the operation, so the declaration should be:
>     procedure Show (Widget : in Gtk_Widget);

You meant Gtk_Widget_Record here.

> and the call should be
>     Show (Window.all);

You can redesign the requirements (decide that show "should be
dispatching"), but that's just avoiding the issue. Surely you can
accept that some subprograms should not be dispatching!

> Probably the real reason the designers of GTK made this an anonymous access 
> parameter is to get that dispatching property. 

no, because the type is 'class. Some other subprograms are
dispatching; that's not the critical issue here.

> It surely has little to do with implicit conversions.

This requires mindreading. In _my_ mind, implicit conversions are a
major reason for this design. 

It would be interesting to hear from the GtkAda designers. I suspect
they would say "that was the only way we could get it to work in Ada
95" or something similar.

> [The fact that you can dispatch on access types in Ada, but only with this 
> bizarre syntax is a highly misguided feature of the language IMHO. It was 
> added only because of the IN OUT parameter restrictions and the insistence 
> of some that that not being able to copy lousy C++ and Java designs directly 
> (rather than better Ada designs not using explicit access types) would harm 
> the language's use in some way.]

It would be interesting to go back and design Ada to accomodate these
issues in a different way.

But I'm trying to work with the language we have.

> Anyway, the real issue that these implicit conversions don't work in many 
> cases that you would expect them to. For instance, inside of Show, if you 
> need to treat the parameter as a value of GTK_Widget:
>
>     Widget_List : GTK_Widget;
>
>     Widget_List := Widget; -- Illegal. Anon to Named conversions not 
> implicit!
>     Widget_List := GTK_Widget(Widget);

Which is as it should be, for accessiblity reasons. If you do this,
you have a potential accessibility error, which is bad. So it
certainly should not be implicit.

> Indeed, the only reason (in Ada 95) why the anonymous access conversions are 
> implicit is the obvious problem that they don't have a name! Else I think 
> they would have in fact required a conversion.
>
> It's possible that we'd going to fix this conversion problem in the next 
> version of Ada, but we will have to do more study to ensure that we don't 
> introduce a show-stopping incompatibility.

Or introduce lots of "unnecessary" explicit conversions. I hope that
discussion will be on the ada-comment list, where I can monitor and/or
participate.

>> I've been struggling with this issue in OpenToken, and settled on
>> using 'access Record_Type' as the best compromise. Now I just need to add
>> 'constant' in all the right places; that wasn't allowed in Ada 95,
>> when OpenToken was first written.
>
> That may have made sense in Ada 95, but now it is a terrible choice, as it 
> forces the user to do all memory management on the objects. 

Yes. But in this case, the intent is to declare a few objects on the
heap, and leave them there forever. Some of them can be on the stack
in some simple situations. 

> If you force passing access-to-objects, you cannot store the objects
> in a container and allow the container to do the storage management.

yes, but it's not a problem in practice. For other applications, it
might be more of a problem.

> (It also requires adding a huge amount of noise to stack-allocated
> objects -- the need to declare everything "aliased" and 'access on
> every call -- and the use of stack-allocated objects should always
> be a primary goal. Why OpenToken cannot be designed to usually use
> stack-allocated objects escapes me...)

Here's one example. We have a grammar:

L -> E  EOF
E -> T {+ T}
T -> F {* F}
F -> ( E )  
F -> integer

This has recursion in one place; E refers to itself via T and F.

Recursion in a data structure requires pointers. 

More complex grammars have more recursion, and require more pointers. 

OpenToken allows for the most complex grammars, so it assumes pointers
for all grammars.

If you use Ada.Containers, you can replace the pointers with
Cursor values, but that just complicates things; at this level, Cursor
values are just pointers with a different (and more cumbersome) syntax.

You will probably say "Cursor values are _safe_, pointers are not".
That's true. I just don't find the extra safety to be worth the
hassle.

But to be fair, I have not tried to implement something as complex as
OpenToken using only Ada.Containers. So maybe the hassle isn't as bad
as I think it will be; what I know how to do is _always_ easier than
something I haven't tried yet :).

I have used Ada.Containers in a small project. It was a mind-bending
experience, but the result is elegant.

>> In some cases, I have both class-wide and primitive operations:
>>
>>   type Instance is abstract tagged private;
>>   subtype Class is Instance'Class;
>>   type Handle is access all Class;
>>
>>   function Name (Token : in Instance) return String is abstract;
>>
>>   --  Dispatching calls to Name
>>   function Name_Dispatch (Token : in Class) return String;
>>   function Name_Dispatch (Token : access constant Instance'Class) return 
>> String;
>>
>> That gives the best of all cases, at the expense of writting more
>> code.
>
> Let me get this straight. You declare an extra routine with a much longer 
> name and a complex (and expensive at runtime call-sequence) in order that 
> you can avoid writing ".all" periodically??

That's not the only, or even the main, reason. There were many places
where I wrote "Name (foo)" that turned out to be not dispatching, when
I wanted it to be dispatching. That was confusing, and difficult to
track down. Hence the longer name.

I have Emacs programmed to put in (or take out) the .all where
necessary (in response to compiler errors), so that's not a big deal
for me.

> That is, if you have
>     type Any_Instance_Class is access all Instance'Class;
>     An_Instance : Any_Instance_Class;
>
> (and appropriate visibility),
>
> you would rather write:
>     Name_Dispatch (An_Instance);
>
> rather than
>     Name (An_Instance.all);

Yes, because then I can be _sure_ it is dispatching. In most cases,
the immediate argument is _not_ classwide, but is in fact a parent
type view of a descendant type object, so I want the call to dispatch,
to get the full descendant type object name.

Saving the .all is gravy.

> Moreover, if you are using Ada 2005, you probably should write:
>    An_Instance.Name
>
> And you don't even need the .all anymore! (Nor do you need "appropriate 
> visibility"!)

That feels like going over to the dark side of C++, so I've been
avoiding it. But I admit it is attractive sometimes. But it doesn't
address the issue of forcing dispatching.

>>> And if you're going to store the access parameter in a global
>>> structure, you'll get a runtime error if you try to store a dangling
>>> reference, so it's best to use a global access type so that
>>> accessibility level errors are caught at compile time.
>>
>> This is a problem; people keep stumbling across it. Still, you get
>> used to it after a while, and stop trying to pass 'access of a local
>> variable.
>
> One would hope that designers of Ada interfaces would talk more care for the 
> users than to say "they get used to it after a while". That sort of ticking 
> time bomb (that easily can be missed in unit testing!) is very adverse to 
> the goals of safety espoused by many in this group (including me!). It has 
> the potential of making Ada into just another programming language.

Yes. 

I did say using "access" was a compromise. I can't go back and
redesign Ada. Nor do I want to reimplement OpenToken to take full
advantage of Ada 2005 features.

I agree that new projects, that are not bound by Ada 95 heritage,
should strive harder to use Ada.Containers and avoid access
parameters. 

-- 
-- Stephe



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

* Re: Access types as parameters
  2009-08-12  2:22                 ` Stephen Leake
@ 2009-08-13  1:06                   ` Randy Brukardt
  2009-08-13  8:34                     ` Niklas Holsti
  2009-08-18 12:22                     ` Stephen Leake
  0 siblings, 2 replies; 31+ messages in thread
From: Randy Brukardt @ 2009-08-13  1:06 UTC (permalink / raw)


"Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message 
news:uprb1epp8.fsf@stephe-leake.org...
> "Randy Brukardt" <randy@rrsoftware.com> writes:
>> "Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message
>> news:u63dkb12m.fsf@stephe-leake.org...
>> ...
>>> Most GTK subprograms do need access values; the same is true of any
>>> system that deals with derived types at multiple levels. The only
>>> library-level object that can hold any type in the hierarchy is a
>>> class-wide pointer. Lists of objects must hold pointers, etc.
>>
>> All of the indefinite containers can hold class-wide objects. No 
>> (explicit)
>> pointers are needed.
>
> You mean the language defined containers in Ada.Containers.*. Yes, the
> pointers are not explicit, but they are needed in the bodies. That's
> how the language works.

Not necessarily, in that Ada.Containers does not need to be implemented in 
Ada. Some other magic could have been used.

But in any case, any implementation pointers are hidden, not exposed in the 
interface. The fact that they might exist ought to be irrelevant.

> Gtk has its own container hierarchy. So it needs pointers.

Which would be misguided in a new design, but obviously it wouldn't make 
sense to try to replace an old working design just for this reason.

>> One of the goals for the next revision of Ada is that the vast majority 
>> of
>> reasonable data structures can be reasonably written with the
>> Ada.Containers.
>
> I agree this is a reasonable approach for many applications, but
> trying to use Ada.Containers to implement the Gtk container hierarchy
> would be a nightmare.

You surely don't *implement* some old containers with Ada.Containers, you 
replace them. And surely you have to get rid of all access types from the 
interfaces if you want to do so. This is not the sort of project that anyone 
is likely to do on existing code (for good reasons). It's solely intended 
for new code.

>> In particular, the multiway tree container can be used to model
>> almost all forms for tree structure (and many graphs as well). That
>> should mean that the need for explicit access types will be greatly
>> reduced when class-wide programming.
>
> This is true, but only if starting from scratch in a new system. And
> it's not always the best solution anyway; each application has
> particular needs.

There is no solution that will handle all needs, but Ada.Containers (or 
Ada.Bounded_Containers) should handle at least 95% of the possible needs. It 
should only be necessary to "roll your own" when at least two of extreme 
performance, extreme control over resource use, and unusual data structures 
are needed. Keep in mind that the Ada.Containers gives you memory management 
for free (much harder to make mistakes) and hopefully will be nearly as easy 
to use in Ada 201Z as writing straight code (given iterator and accessor 
syntax improvements).

At least that's the intent. A certain amount of FUD and unfamilarity will 
probably prevent people from using the containers as much as they ought to 
be.

>>> A reason to use 'access Record_Type' instead of 'in Access_Type' is to
>>> avoid explicit type conversions.
>>
>> This is a terrible reason: explicit conversions show when you are 
>> converting
>> accesses from one "domain of use" to another.
>
> Not in this case. The domain is always "some Gtk object". Or "some
> OpenToken object".

That's broken. If OpenToken is parsing more than one (unrelated) stream, it 
makes perfect sense to keep the objects related to those parsings in 
different domains. If you are not allowing this, you are restricting the 
applicability of the library unnecessarily.

(It's not as obvious that this matters to GTK; I think it does -- we 
certainly allow this sort of thing in Claw -- but a case can be made that 
there is only one GUI to write too.)

>> (I'm thinking of each named access type as a "domain-of-use". It
>> only makes sense to have multiple named access types for a
>> particular designated type if there are different "domains-of-use"
>> [such as different storage pools or disjoint data structures].)
>
> GtkAda and OpenToken don't have different named access types for one
> type; they have different named access types for types in a hierarchy.

IMHO, the library (specification) shouldn't have *any* access types, named 
or otherwise. [I realize this is an ideal; occassionally, you need reference 
semantics for a function result and thus have to use an access type. But you 
never need to do that for a parameter.] In any case, I'm talking about the 
client's access types, generally one per domain-of-use.

> That allows the user to have collections of "any Gtk object", and
> collections of "Gtk windows" or "my dialog objects". All of these
> occur in a real Gtk application, and all need their own access type.

If the user needs a collection of "any GTK object", they can create one with 
their own access type, or with an appropriate Ada.Containers instance. You 
don't need to put that into the interface of the library to get that sort of 
functionality. When you do, you just greatly limit the user's memory 
management options.

(And I realize that no one is going to change GTK anytime soon; I'm talking 
about how libraries ought to be when built from scratch. But no one should 
be under any illusions that GTK is a good design, especially for current 
versions of Ada.)

>> The type conversions make it clear when you are converting from one
>> "domain-of-use" to another, something that should be rare.
>
> "converting" from one level in the type hierarchy to another is not
> rare. That's part of the point of a type hierarchy. Most of the time,
> it's just a view conversion, not a "real" conversion.

If you're changing levels, you are not "converting" at all, because all of 
these use the same named access type. (Such named access types will almost 
always be access-to-class-wide.) So there is no conversion at all going on.

Indeed, if you have a type hierarchy, you have OOP, and the way you go 
between levels is with dispatching. Again, there is no conversions.

I really don't understand why some designers want to declare access types 
all over the place. There should be exactly one for each "domain-of-use" 
(such as "any GTK window for the local terminal"). But "domain-of-use" is a 
client decision, not something the library should be deciding.

> To be convincing, I'd have to post an example from Gtk or OpenToken
> written both ways; maybe later I'll find time for that.

Yeah, I know that we are both talking rather hypothetically, and we may have 
rather different examples in mind.

>> Anonymous access types hide that, and make it essentially impossible to
>> determine what the storage pool of an access is -- meaning that most 
>> values
>> that pass through anonymous access cannot be deallocated even if 
>> converted
>> to a named type (because there is no way to know if it is the *right* 
>> named
>> type). [Technically, they can be deallocated, but the program is 
>> erroneous
>> and may do anything.]
>
> That's true. But it just forces better application design.
>
> Deallocating should be done by the same chunk of code that allocates,
> so this shouldn't be an issue.

Given that it is the client that determines have memory management is done 
for a particular use, you are right to an extent. But only if you never try 
to store the anonymous access in any way, which in my experience with Claw 
is an impossible restriction.

> That is how OpenToken works internally.

The client should be determining the memory management of the visible 
objects (only it can know if stack allocation, container allocator, or full 
dynamic allocation is needed). Hidden stuff, of course, had better be 
managed by the library.

...
> You can redesign the requirements (decide that show "should be
> dispatching"), but that's just avoiding the issue. Surely you can
> accept that some subprograms should not be dispatching!

It's interesting that later you essentially said that Show should be 
primitive (which is the same as dispatching in Ada for OO types like these).

But be that as it may, my answer is that for OO types (tagged types), no, I 
think that all routines should either be class-wide or dispatching. A 
routine which is neither is highly suspicious and indicative of a bug. (I 
know that there is an issue with constructors, but I actually prefer them to 
be dispatching so that they are required to be overridden, either with 
something useful or with "raise Program_Error". I wouldn't insist on this 
last part in a programming guide, however.)

...
>> It surely has little to do with implicit conversions.
>
> This requires mindreading. In _my_ mind, implicit conversions are a
> major reason for this design.

Then it is a load of crap. IMHO. (But no sense in us arguing it...)

> It would be interesting to hear from the GtkAda designers. I suspect
> they would say "that was the only way we could get it to work in Ada
> 95" or something similar.

Right.

...
>>> I've been struggling with this issue in OpenToken, and settled on
>>> using 'access Record_Type' as the best compromise. Now I just need to 
>>> add
>>> 'constant' in all the right places; that wasn't allowed in Ada 95,
>>> when OpenToken was first written.
>>
>> That may have made sense in Ada 95, but now it is a terrible choice, as 
>> it
>> forces the user to do all memory management on the objects.
>
> Yes. But in this case, the intent is to declare a few objects on the
> heap, and leave them there forever. Some of them can be on the stack
> in some simple situations.

Claw had a similar intent, yet we decided to let the client make the memory 
management decisions. Thus you can now use an Ada.Containers instance to 
make a list of windows, and we didn't have to make any changes to Claw to 
accomidate this use.

>> If you force passing access-to-objects, you cannot store the objects
>> in a container and allow the container to do the storage management.
>
> yes, but it's not a problem in practice. For other applications, it
> might be more of a problem.

>> (It also requires adding a huge amount of noise to stack-allocated
>> objects -- the need to declare everything "aliased" and 'access on
>> every call -- and the use of stack-allocated objects should always
>> be a primary goal. Why OpenToken cannot be designed to usually use
>> stack-allocated objects escapes me...)
>
> Here's one example. We have a grammar:
>
> L -> E  EOF
> E -> T {+ T}
> T -> F {* F}
> F -> ( E )
> F -> integer
>
> This has recursion in one place; E refers to itself via T and F.
>
> Recursion in a data structure requires pointers.

Sure, but you surely don't have to expose any of those pointers in the 
interface! At worst, you expose "handles" or "cursors" in the manner that 
the containers do, in order that you don't expose clients to all of the 
erroneous behavior that comes from the misuse of pointers.

> More complex grammars have more recursion, and require more pointers.
>
> OpenToken allows for the most complex grammars, so it assumes pointers
> for all grammars.

And I guess that's my objection.

> If you use Ada.Containers, you can replace the pointers with
> Cursor values, but that just complicates things; at this level, Cursor
> values are just pointers with a different (and more cumbersome) syntax.

No, Cursors are *abstracted* pointers, with the details hidden. Sure, hiding 
*always* complicates things, but it also buys you clearer interfaces (no 
questions about who is supposed to allocate things) and potentially extra 
checking (dangling cursors can usually be detected cheaply, whereas dangling 
pointers usually just corrupt memory).

In any case, the Ada 201Z syntax can be as simple as one extra name. No big 
deal. Yes, I know it will be a while before we have that in hand, but the 
extra syntax now isn't that much for the dangling pointer detection that you 
get (at least from most containers implementations).

> You will probably say "Cursor values are _safe_, pointers are not".
> That's true. I just don't find the extra safety to be worth the
> hassle.

For me, the whole reason for using Ada in the first place is that it finds 
most of my mistakes for me. The ones that cause the most trouble are usually 
dangling pointers problems. If I can use a way to detect them, I'll never 
have to find a bug again. ;-) ;-) That's surely worth a little bit of extra 
work.

> But to be fair, I have not tried to implement something as complex as
> OpenToken using only Ada.Containers. So maybe the hassle isn't as bad
> as I think it will be; what I know how to do is _always_ easier than
> something I haven't tried yet :).

The main problem with the Ada 2005 containers is the difficulty of updating 
an element in place. We think that we have solved that fairly cheaply for 
Ada 201Z (although the idiom is pretty strange and I worry that users won't 
be able to figure out how it works).

> I have used Ada.Containers in a small project. It was a mind-bending
> experience, but the result is elegant.

Great. I haven't had the opportunity yet (unfortunately). I've been 
maintaining existing stuff for the most part.

>>> In some cases, I have both class-wide and primitive operations:
>>>
>>>   type Instance is abstract tagged private;
>>>   subtype Class is Instance'Class;
>>>   type Handle is access all Class;
>>>
>>>   function Name (Token : in Instance) return String is abstract;
>>>
>>>   --  Dispatching calls to Name
>>>   function Name_Dispatch (Token : in Class) return String;
>>>   function Name_Dispatch (Token : access constant Instance'Class) return
>>> String;
>>>
>>> That gives the best of all cases, at the expense of writting more
>>> code.
>>
>> Let me get this straight. You declare an extra routine with a much longer
>> name and a complex (and expensive at runtime call-sequence) in order that
>> you can avoid writing ".all" periodically??
>
> That's not the only, or even the main, reason. There were many places
> where I wrote "Name (foo)" that turned out to be not dispatching, when
> I wanted it to be dispatching. That was confusing, and difficult to
> track down. Hence the longer name.

This doesn't make sense to me, although I realize Ada 95 had some really 
serious problems in this area. A call to Name is *always* a dispatching 
call. The issue could have been that you are getting static binding, but 
that is almost always a good thing. You almost never want redispatching, and 
in the rare cases where you do want it, you need to ask for it explicitly 
with a type conversion.


> I have Emacs programmed to put in (or take out) the .all where
> necessary (in response to compiler errors), so that's not a big deal
> for me.
>
>> That is, if you have
>>     type Any_Instance_Class is access all Instance'Class;
>>     An_Instance : Any_Instance_Class;
>>
>> (and appropriate visibility),
>>
>> you would rather write:
>>     Name_Dispatch (An_Instance);
>>
>> rather than
>>     Name (An_Instance.all);
>
> Yes, because then I can be _sure_ it is dispatching. In most cases,
> the immediate argument is _not_ classwide, but is in fact a parent
> type view of a descendant type object, so I want the call to dispatch,
> to get the full descendant type object name.

You must be doing something seriously wrong, because you always get 
(dynamic) dispatching when the argument is class-wide, and otherwise you get 
static binding. I thought you were using a bunch of access-to-class-wide 
types, so how you could *not* get dispatching is beyond me. Well, unless you 
are converting to the specific type rather than the class-wide type when 
toward the leaves of the tree -- Ada is pretty picky about this! It is 
critical to remember that T and T'Class are different types in Ada with 
different properties and you can't use the interchangably.

> Saving the .all is gravy.
>
>> Moreover, if you are using Ada 2005, you probably should write:
>>    An_Instance.Name
>>
>> And you don't even need the .all anymore! (Nor do you need "appropriate
>> visibility"!)
>
> That feels like going over to the dark side of C++, so I've been
> avoiding it. But I admit it is attractive sometimes. But it doesn't
> address the issue of forcing dispatching.

If you really need to force dispatching, convert to the appropriate 
class-wide type:

    Some_Type'Class(An_Instance.all).Name

or

    Name (Some_Type'Class(An_Instance.all));

(we have to stick the .all in, unfortunately). The use of 'Class is what 
forces dispatching in Ada. But this should be pretty rare. It's certainly 
not necessary in this example as An_Instance is an access-to-class-wide type 
and thus the call is definitely dispatching.

In any case, it should be pretty rare in OO code where you need to "force" 
dispatching. Usually, Ada does the right thing automatically. The main 
problem I had with Claw was using type conversions to force the correct 
resolution of a call for the visibility I had, and that can screw up the 
dispatching. The solution (now) is often is the use of the prefix notation, 
which pretty much ignores visibility issues and thus will resolve in many 
more cases. I suspect that you ran into the same problem, but fixed it by 
clogging up the interfaces (which does work, of course, just not something 
I'd recommend).

                                      Randy.





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

* Re: Access types as parameters
  2009-08-13  1:06                   ` Randy Brukardt
@ 2009-08-13  8:34                     ` Niklas Holsti
  2009-08-13  9:15                       ` Dmitry A. Kazakov
  2009-08-14  4:07                       ` Randy Brukardt
  2009-08-18 12:22                     ` Stephen Leake
  1 sibling, 2 replies; 31+ messages in thread
From: Niklas Holsti @ 2009-08-13  8:34 UTC (permalink / raw)


Randy Brukardt wrote:

> You almost never want redispatching

That's interesting as my experience is the opposite: Most of the calls 
between primitive methods in my code use redispatching.

My tagged types tend to have a hierarchy of primitive operations, for 
example an Overall_Method that implements some functionality by a 
sequence of calls to a Detail_Method. Some derived types override 
Detail_Method, so the call from Overall_Method to Detail_Method must 
redispatch. Some other (or the same) derived types override 
Overall_Method, so it cannot be class-wide.

Perhaps the frequency of redispatching depends on the application 
domain, or on personal programming style. The frequency of redispatching 
might me an interesting OO "metric".

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-13  8:34                     ` Niklas Holsti
@ 2009-08-13  9:15                       ` Dmitry A. Kazakov
  2009-08-13 20:13                         ` Niklas Holsti
  2009-08-14  4:07                       ` Randy Brukardt
  1 sibling, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-13  9:15 UTC (permalink / raw)


On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:

> Randy Brukardt wrote:
> 
>> You almost never want redispatching
> 
> That's interesting as my experience is the opposite: Most of the calls 
> between primitive methods in my code use redispatching.

Shudder...

> My tagged types tend to have a hierarchy of primitive operations, for 
> example an Overall_Method that implements some functionality by a 
> sequence of calls to a Detail_Method. Some derived types override 
> Detail_Method, so the call from Overall_Method to Detail_Method must 
> redispatch. Some other (or the same) derived types override 
> Overall_Method, so it cannot be class-wide.

When this happens to me I always consider it as a strong indication to
re-design.

> Perhaps the frequency of redispatching depends on the application 
> domain, or on personal programming style. The frequency of redispatching 
> might me an interesting OO "metric".

Yes. I think the target shall be strictly 0%.

I consider re-dispatch is an indication of a language / design problem,
because it is a hidden type error. An argument goes as follows. Once you
have dispatched to some type T, the corresponding controlling parameter is
of this type. Let we converted it to a class. If now it dispatches again to
T, then that was just wasting time, if it dispatches to S /= T then it is a
type error.

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



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

* Re: Access types as parameters
  2009-08-13  9:15                       ` Dmitry A. Kazakov
@ 2009-08-13 20:13                         ` Niklas Holsti
  2009-08-13 21:07                           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-13 20:13 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:
> 
>> Randy Brukardt wrote:
>>
>>> You almost never want redispatching
>> That's interesting as my experience is the opposite: Most of the calls 
>> between primitive methods in my code use redispatching.
> 
> Shudder...

Also an interesting reaction.

> I consider re-dispatch is an indication of a language / design problem,
> because it is a hidden type error. An argument goes as follows. Once you
> have dispatched to some type T, the corresponding controlling parameter is
> of this type. Let we converted it to a class. If now it dispatches again to
> T, then that was just wasting time, if it dispatches to S /= T then it is a
> type error.

To me that is a very abstract and formalistic argument that is easily 
weaker than the practical benefits (providing sensible but overridable 
default behaviour while avoding code duplication) that make me use 
redispatching. What is more, a central benefit of inheritance is that an 
object can be viewed as being of parent type T *and also* of derived 
type S, depending on the context and your needs, so I don't accept that 
redispatching "to a different type" implies a type error.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-13 20:13                         ` Niklas Holsti
@ 2009-08-13 21:07                           ` Dmitry A. Kazakov
  2009-08-14  9:27                             ` Niklas Holsti
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-13 21:07 UTC (permalink / raw)


On Thu, 13 Aug 2009 23:13:39 +0300, Niklas Holsti wrote:

> Dmitry A. Kazakov wrote:
>> On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:
>> 
>>> Randy Brukardt wrote:
>>>
>>>> You almost never want redispatching
>>> That's interesting as my experience is the opposite: Most of the calls 
>>> between primitive methods in my code use redispatching.
>> 
>> Shudder...
> 
> Also an interesting reaction.
> 
>> I consider re-dispatch is an indication of a language / design problem,
>> because it is a hidden type error. An argument goes as follows. Once you
>> have dispatched to some type T, the corresponding controlling parameter is
>> of this type. Let we converted it to a class. If now it dispatches again to
>> T, then that was just wasting time, if it dispatches to S /= T then it is a
>> type error.
> 
> To me that is a very abstract and formalistic argument that is easily 
> weaker than the practical benefits (providing sensible but overridable 
> default behaviour while avoding code duplication) that make me use 
> redispatching.

If some formal premises are not satisfied (a type consistency is one of
them) then it is meaningless to talk about benefits. In an extreme case,
you might have an erroneous program that occasionally yields a correct
result. Would you see a practical benefit here?

> What is more, a central benefit of inheritance is that an 
> object can be viewed as being of parent type T *and also* of derived 
> type S, depending on the context and your needs, so I don't accept that 
> redispatching "to a different type" implies a type error.

An object of the type S is already substitutable for T in the primitive
operations of T and the operations of T'Class. Why do you want to
re-dispatch? Re-dispatch means that an object pulls a trail of its history
of type conversions (view conversion is a case of) throughout all
substitutions of the object. This is a sufficiently more complex
programming model, which semantics is doubtful. Instead of plain type T you
have "S as T", or "S from R seen as T", and so on. That eliminates the
advantage of clean Ada model that distinguishes T and T'Class.

Your initial example was about certain decomposition of operations into
class-wide and primitive, so that a primitive operation would call to
class-wide ones. My point that such cases should be attributed to poor
design or maybe to a language problem. One argument I gave was type
inconsistency. There are also practical arguments that dispatching calls
are slower, that re-dispatch requires referential semantics, that there
would be no chance to have classes of by-value types like Integer etc.

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



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

* Re: Access types as parameters
  2009-08-13  8:34                     ` Niklas Holsti
  2009-08-13  9:15                       ` Dmitry A. Kazakov
@ 2009-08-14  4:07                       ` Randy Brukardt
  2009-08-14 10:22                         ` Niklas Holsti
  1 sibling, 1 reply; 31+ messages in thread
From: Randy Brukardt @ 2009-08-14  4:07 UTC (permalink / raw)


"Niklas Holsti" <niklas.holsti@tidorum.invalid> wrote in message 
news:4a83d018$0$26303$4f793bc4@news.tdc.fi...
> Randy Brukardt wrote:
>
>> You almost never want redispatching
>
> That's interesting as my experience is the opposite: Most of the calls 
> between primitive methods in my code use redispatching.
>
> My tagged types tend to have a hierarchy of primitive operations, for 
> example an Overall_Method that implements some functionality by a sequence 
> of calls to a Detail_Method. Some derived types override Detail_Method, so 
> the call from Overall_Method to Detail_Method must redispatch. Some other 
> (or the same) derived types override Overall_Method, so it cannot be 
> class-wide.

It seems to me that one or the other of these routines ought to be 
class-wide, the fact that neither can be is a design problem. But YMMV.

The primary reason that you don't (usually) want redispatch is that it is 
hard to guarentee the semantics of a routine if it re-dispatches and thus 
calls routines with unknown semantics. For Ada predefined libraries, we went 
so far as to create a rule that calls do *not* redispatch - A(3.1/3) (You'll 
have to look in the draft RM to see that one, since it was added in response 
to a question on Ada 2005: see 
http://www.adaic.com/standards/1zrm/html/RM-A.html). To quote:

"For a descendant of a language-defined tagged type, the implementation 
shall ensure that each inherited language-defined subprogram behaves as 
described in this International Standard. In particular, overriding a 
language-defined subprogram shall not alter the effect of any inherited 
language-defined subprogram."

In short, don't redispatch in language-defined routines.

Re-dispatching is really only safe semantically if you can guarentee that 
the functionality of any overriding is a superset of that of the base 
operation. But of course there is no way to guarentee that in Ada; even if 
you intend it to be true and write the code with calls to the parent 
routine, raising an exception can cause it to become false (by skipping the 
parent routine).

Of course, sometimes the semantics aren't very firmly defined, and 
redispatching works fine. As I said, YMMV.

                       Randy.






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

* Re: Access types as parameters
  2009-08-13 21:07                           ` Dmitry A. Kazakov
@ 2009-08-14  9:27                             ` Niklas Holsti
  2009-08-14 10:36                               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-14  9:27 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Thu, 13 Aug 2009 23:13:39 +0300, Niklas Holsti wrote:
> 
>> Dmitry A. Kazakov wrote:
>>> On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:
>>>
>>>> Randy Brukardt wrote:
>>>>
>>>>> You almost never want redispatching
>>>> That's interesting as my experience is the opposite: Most of the calls 
>>>> between primitive methods in my code use redispatching.
>>> Shudder...
>> Also an interesting reaction.
>>
>>> I consider re-dispatch is an indication of a language / design problem,
>>> because it is a hidden type error. An argument goes as follows. Once you
>>> have dispatched to some type T, the corresponding controlling parameter is
>>> of this type. Let we converted it to a class. If now it dispatches again to
>>> T, then that was just wasting time, if it dispatches to S /= T then it is a
>>> type error.
>> To me that is a very abstract and formalistic argument that is easily 
>> weaker than the practical benefits (providing sensible but overridable 
>> default behaviour while avoding code duplication) that make me use 
>> redispatching.
> 
> If some formal premises are not satisfied (a type consistency is one of
> them) then it is meaningless to talk about benefits. In an extreme case,
> you might have an erroneous program that occasionally yields a correct
> result. Would you see a practical benefit here?

As I said later in the message to which you are replying, I don't agree 
that redispatching implies a type inconsistency.

Do you use the word "erroneous" in the formal Ada sense, "erroneous 
execution"? Then could you please explain what kind of erroneous 
execution redispatching can cause, that cannot occur without using 
redispatching? As far as I understand, redispatching itself is safe; 
erroneous execution can only come from code in the operations that are 
invoked.

If your argument is just that a program with redispatching is more 
complex and thus more likely to contain programming errors, I partly 
agree; redispatching makes the sequence of invoked operations more 
dynamic and thus possibly harder to grasp and make correct. But this 
argument applies as well to any kind of run-time dispatching, not just 
to *re*-dispatching.

> Your initial example was about certain decomposition of operations into
> class-wide and primitive, so that a primitive operation would call to
> class-wide ones.

No, I think you mistunderstood my example, perhaps because I did not 
explain it clearly enough. The two operations in my example are both 
primitive, and can be independently overridden with behaviour suitable 
to derived types. The operations call each other with redispatching to 
activate the most suitable behaviour of each operation for the actual 
type of object.

> My point that such cases should be attributed to poor
> design

In the mind of the beholder, I think. You haven't shown *why* this 
design is "poor", except by your own design rules.

> There are also practical arguments that dispatching calls
> are slower,

I don't expect to get something for free. Dispatching calls are slower 
than statically bound calls, right? But you still use dispatching calls 
when needed, right?

> that re-dispatch requires referential semantics,

I don't see why, but I don't really care, as I have accepted (albeit 
reluctantly) that I need referential semantics anyway.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-14  4:07                       ` Randy Brukardt
@ 2009-08-14 10:22                         ` Niklas Holsti
  0 siblings, 0 replies; 31+ messages in thread
From: Niklas Holsti @ 2009-08-14 10:22 UTC (permalink / raw)


Randy Brukardt wrote:
> "Niklas Holsti" <niklas.holsti@tidorum.invalid> wrote in message 
> news:4a83d018$0$26303$4f793bc4@news.tdc.fi...
>> Randy Brukardt wrote:
>>
>>> You almost never want redispatching
>> That's interesting as my experience is the opposite: Most of the calls 
>> between primitive methods in my code use redispatching.
>>
>> My tagged types tend to have a hierarchy of primitive operations, for 
>> example an Overall_Method that implements some functionality by a sequence 
>> of calls to a Detail_Method. Some derived types override Detail_Method, so 
>> the call from Overall_Method to Detail_Method must redispatch. Some other 
>> (or the same) derived types override Overall_Method, so it cannot be 
>> class-wide.
> 
> It seems to me that one or the other of these routines ought to be 
> class-wide, the fact that neither can be is a design problem. But YMMV.

Thanks for understanding that YMMV.

Perhaps I can explain my context a bit more. Think of a class of 
problems that can be solved with a divide-and-conquer approach (this is 
not exactly my application, but it is similar). There are thus two 
operations: "Divide" a problem into cases, and "Conquer" a given case.

There is a general, but rather slow, way to Divide any problem in the 
class, so the root type implements a Divide operation that applies this 
general procedure and then calls Conquer for each case that it generates.

Some special kinds of problems (derived types and subclasses) have 
faster, specialized forms of Divide, so Divide is overridden for these 
types.

Each kind of problem (derived type) has a specific Conquer method, and 
so each derived type overrides the Conquer operation (which is usually 
abstract in the root type, in fact, but that is incidental).

> The primary reason that you don't (usually) want redispatch is that it is 
> hard to guarentee the semantics of a routine if it re-dispatches and thus 
> calls routines with unknown semantics.

Agreed, but that argument applies as well to any kind of run-time 
dispatching, not just *re*-dispatching, and thus for example to 
dispatching calls from class-wide operations.

If you have a class C with primitive operations Frobnicate and Dazzlify, 
which are expected to fulfil some application requirements, it is surely 
part of the programming task to ensure that each overriding Frobnicate 
or Dazzlify has the right semantics for its type, in whatever context it 
is called, whether using "basic" dispatching or redispatching. Assuming, 
of course, that the calls obey whatever preconditions are required for 
those operations.

> For Ada predefined libraries, we went 
> so far as to create a rule that calls do *not* redispatch - A(3.1/3)
> (You'll  have to look in the draft RM to see that one, since it was
> added in  response  to a question on Ada 2005:

That question came from me, I think (15 Feb 2007).

> see http://www.adaic.com/standards/1zrm/html/RM-A.html). To quote:
> 
> "For a descendant of a language-defined tagged type, the implementation 
> shall ensure that each inherited language-defined subprogram behaves as 
> described in this International Standard. In particular, overriding a 
> language-defined subprogram shall not alter the effect of any inherited 
> language-defined subprogram."

Nice to see that my question led to something, thanks.

> In short, don't redispatch in language-defined routines.

That was the solution I suggested in my question on Ada-Comment, as 
being the simplest solution. But it is not the only possible solution; 
another solution would have been to specify the redispatching that 
language-defined routines must or may do. As Tucker Taft commented, 
"redispatching must be seen as part of the *specification* of the 
primitive operation of a tagged type".

Of course, in my own code I carefully specify which operations use 
redispatching :-)

> Re-dispatching is really only safe semantically if you can guarentee that 
> the functionality of any overriding is a superset of that of the base 
> operation. But of course there is no way to guarentee that in Ada;

Unfortunately, Ada does not guarantee that you write a correct program, 
period. (But I do find that she helps.)

> Of course, sometimes the semantics aren't very firmly defined, and 
> redispatching works fine.

I would say, rather, that redispatching works fine *if* the semantics of 
the primitive operations *are* firmly defined, with redispatching in mind.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-14  9:27                             ` Niklas Holsti
@ 2009-08-14 10:36                               ` Dmitry A. Kazakov
  2009-08-14 16:03                                 ` Niklas Holsti
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-14 10:36 UTC (permalink / raw)


On Fri, 14 Aug 2009 12:27:02 +0300, Niklas Holsti wrote:

> Dmitry A. Kazakov wrote:
>> On Thu, 13 Aug 2009 23:13:39 +0300, Niklas Holsti wrote:
>> 
>>> Dmitry A. Kazakov wrote:
>>>> On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:
>>>>
>>>>> Randy Brukardt wrote:
>>>>>
>>>>>> You almost never want redispatching
>>>>> That's interesting as my experience is the opposite: Most of the calls 
>>>>> between primitive methods in my code use redispatching.
>>>> Shudder...
>>> Also an interesting reaction.
>>>
>>>> I consider re-dispatch is an indication of a language / design problem,
>>>> because it is a hidden type error. An argument goes as follows. Once you
>>>> have dispatched to some type T, the corresponding controlling parameter is
>>>> of this type. Let we converted it to a class. If now it dispatches again to
>>>> T, then that was just wasting time, if it dispatches to S /= T then it is a
>>>> type error.
>>> To me that is a very abstract and formalistic argument that is easily 
>>> weaker than the practical benefits (providing sensible but overridable 
>>> default behaviour while avoding code duplication) that make me use 
>>> redispatching.
>> 
>> If some formal premises are not satisfied (a type consistency is one of
>> them) then it is meaningless to talk about benefits. In an extreme case,
>> you might have an erroneous program that occasionally yields a correct
>> result. Would you see a practical benefit here?
> 
> As I said later in the message to which you are replying, I don't agree 
> that redispatching implies a type inconsistency.
> 
> Do you use the word "erroneous" in the formal Ada sense, "erroneous 
> execution"? Then could you please explain what kind of erroneous 
> execution redispatching can cause, that cannot occur without using 
> redispatching? As far as I understand, redispatching itself is safe; 
> erroneous execution can only come from code in the operations that are 
> invoked.

It was merely a counterexample to "benefit" over "formalism". "Benefit" is
bounded by "formalism", not otherwise. If we consider re-dispatch doubtful
for formal reasons, that overrides any benefits (or we change the
formalism).

> If your argument is just that a program with redispatching is more 
> complex and thus more likely to contain programming errors, I partly 
> agree; redispatching makes the sequence of invoked operations more 
> dynamic and thus possibly harder to grasp and make correct. But this 
> argument applies as well to any kind of run-time dispatching, not just 
> to *re*-dispatching.

I disagree. Dispatching does not make it more complex assuming that all
implementations of the primitive operation are conform the interface. You
write the client program in terms of another type, of the type T'Class.
There is nothing automatically more complex in T'Class than in T. 

>> Your initial example was about certain decomposition of operations into
>> class-wide and primitive, so that a primitive operation would call to
>> class-wide ones.
> 
> No, I think you mistunderstood my example, perhaps because I did not 
> explain it clearly enough. The two operations in my example are both 
> primitive, and can be independently overridden with behaviour suitable 
> to derived types. The operations call each other with redispatching to 
> activate the most suitable behaviour of each operation for the actual 
> type of object.

Yes, I understand this. The question is why it was decomposed in this way.
If one operation of the type T calls another operation of the same type.
Why should it dispatch. When you call an operation of the type S on T, that
is a type error to me. You have circumvented the type system, by an
explicit type conversion of T to T'Class. Do not we agree that type
conversions should be avoided?

>> My point that such cases should be attributed to poor
>> design
> 
> In the mind of the beholder, I think. You haven't shown *why* this 
> design is "poor", except by your own design rules.

I think I did. Why do you dispatch again, you already checked the tag. The
type is determined, the reader expects the code to correspond that type,
you break this convention. My concern is the semantics of the code.
Semantically a primitive operation has separate bodies for each type in the
class. This gets broken in your design. I.e. it is a third way of how
bodies are decomposed. I may agree that the language could offer more than
one body per class (class-wide) vs. one body per type (primitive). But so
far, a design should conform to the existing model.

>> There are also practical arguments that dispatching calls
>> are slower,
> 
> I don't expect to get something for free. Dispatching calls are slower 
> than statically bound calls, right? But you still use dispatching calls 
> when needed, right?

Yes, but only if I cannot statically specify the type. Re-dispatch happens
at the places where I already determined the type, but for some reasons am
not satisfied with the outcome. Why? Strange...

>> that re-dispatch requires referential semantics,
> 
> I don't see why,

Because of the type tag. You need same representation for T and T'Class to
have T -> T'Class as a view conversion.

> but I don't really care, as I have accepted (albeit 
> reluctantly) that I need referential semantics anyway.

But it means that the design relies on referential semantics and cannot be
rewritten in terms of value semantics. Formally there should be something
fundamentally wrong with that, because there is no reason beyond 1.
optimization issues, 2. outer world communication, why referential
semantics is chosen. Yet we rely on it. Why?

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



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

* Re: Access types as parameters
  2009-08-14 10:36                               ` Dmitry A. Kazakov
@ 2009-08-14 16:03                                 ` Niklas Holsti
  2009-08-15  9:47                                   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-14 16:03 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Fri, 14 Aug 2009 12:27:02 +0300, Niklas Holsti wrote:
> 
>> Dmitry A. Kazakov wrote:
>>> On Thu, 13 Aug 2009 23:13:39 +0300, Niklas Holsti wrote:
>>>
>>>> Dmitry A. Kazakov wrote:
>>>>> On Thu, 13 Aug 2009 11:34:20 +0300, Niklas Holsti wrote:
>>>>>
>>>>>> Randy Brukardt wrote:
>>>>>>
>>>>>>> You almost never want redispatching
>>>>>> That's interesting as my experience is the opposite: Most of the calls 
>>>>>> between primitive methods in my code use redispatching.
>>>>> Shudder...
>>>> Also an interesting reaction.
>>>>
>>>>> I consider re-dispatch is an indication of a language / design problem,
>>>>> because it is a hidden type error. An argument goes as follows. Once you
>>>>> have dispatched to some type T, the corresponding controlling parameter is
>>>>> of this type. Let we converted it to a class. If now it dispatches again to
>>>>> T, then that was just wasting time, if it dispatches to S /= T then it is a
>>>>> type error.
>>>> To me that is a very abstract and formalistic argument that is easily 
>>>> weaker than the practical benefits (providing sensible but overridable 
>>>> default behaviour while avoding code duplication) that make me use 
>>>> redispatching.
>>> If some formal premises are not satisfied (a type consistency is one of
>>> them) then it is meaningless to talk about benefits. In an extreme case,
>>> you might have an erroneous program that occasionally yields a correct
>>> result. Would you see a practical benefit here?
>> As I said later in the message to which you are replying, I don't agree 
>> that redispatching implies a type inconsistency.
>>
>> Do you use the word "erroneous" in the formal Ada sense, "erroneous 
>> execution"? Then could you please explain what kind of erroneous 
>> execution redispatching can cause, that cannot occur without using 
>> redispatching? As far as I understand, redispatching itself is safe; 
>> erroneous execution can only come from code in the operations that are 
>> invoked.
> 
> It was merely a counterexample to "benefit" over "formalism". "Benefit" is
> bounded by "formalism", not otherwise. If we consider re-dispatch doubtful
> for formal reasons, that overrides any benefits (or we change the
> formalism).

Or we don't agree on the formalism, which seems to be the case for you 
and me.

>> If your argument is just that a program with redispatching is more 
>> complex and thus more likely to contain programming errors, I partly 
>> agree; redispatching makes the sequence of invoked operations more 
>> dynamic and thus possibly harder to grasp and make correct. But this 
>> argument applies as well to any kind of run-time dispatching, not just 
>> to *re*-dispatching.
> 
> I disagree. Dispatching does not make it more complex assuming that all
> implementations of the primitive operation are conform the interface. You
> write the client program in terms of another type, of the type T'Class.
> There is nothing automatically more complex in T'Class than in T. 

If you assume that all operation implementations "conform to the 
interface" (including semantics) then redispatching is no more complex 
than dispatching; the effect of the (re)dispatching call is assumed to 
be the desired one, whatever the actual type of the controlling operand(s).

>>> Your initial example was about certain decomposition of operations into
>>> class-wide and primitive, so that a primitive operation would call to
>>> class-wide ones.
>> No, I think you mistunderstood my example, perhaps because I did not 
>> explain it clearly enough. The two operations in my example are both 
>> primitive, and can be independently overridden with behaviour suitable 
>> to derived types. The operations call each other with redispatching to 
>> activate the most suitable behaviour of each operation for the actual 
>> type of object.
> 
> Yes, I understand this. The question is why it was decomposed in this way.
> If one operation of the type T calls another operation of the same type.
> Why should it dispatch.

It does *not* call another operation of the same type; it calls another 
operation of the same *class*. That is what redispatching means, and why 
the call contains a conversion T->T'Class.

> When you call an operation of the type S on T, that
> is a type error to me. You have circumvented the type system, by an
> explicit type conversion of T to T'Class. Do not we agree that type
> conversions should be avoided?

I agree that type conversions "should" be avoided, but good grief, 
sometimes you need them to express something, as in this case, because 
Ada specifically uses the conversion T->T'Class to invoke dynamic 
(re)dispatching. And of course this conversion is safe and does not in 
any way "circumvent" the type system.

>>> My point that such cases should be attributed to poor
>>> design
>> In the mind of the beholder, I think. You haven't shown *why* this 
>> design is "poor", except by your own design rules.
> 
> I think I did.

I disagree (maybe we should have an abbreviation for this, as it seems 
to occur frequently :-)

> Why do you dispatch again, you already checked the tag.

Because this gives me the functionality I want, of course.

> The type is determined,

Of course not, only the point (within the class) of the implementation 
of the currently executing, (possibly) inherited operation (the caller) 
is determined. The actual type, as you well know, is any type in 
T'Class, although it is written "T" in the operation profile.

> the reader expects the code to correspond that type,
> you break this convention.

That depends on the "reader". My convention is different from yours, and 
I think my convention corresponds more closely to the (full) Ada 
language, while yours assumes a subset of Ada that follows your design 
rules.

> My concern is the semantics of the code.
> Semantically a primitive operation has separate bodies for each type in the
> class.

Yes, but they are represented by a smaller (sparse) number of bodies in 
the program text, with the "missing" bodies supplied by inheritance. And 
that is why the actual type of the actual parameter object is some type 
in T'Class, not always T (as you well know...)

> This gets broken in your design. I.e. it is a third way of how
> bodies are decomposed. I may agree that the language could offer more than
> one body per class (class-wide) vs. one body per type (primitive). But so
> far, a design should conform to the existing model.

I fail to understand that. You seem to be claiming that the Ada "model" 
does not really include redispatching, which is nonsense to me.

> Yes, but only if I cannot statically specify the type. Re-dispatch happens
> at the places where I already determined the type,

The type is *not* already determined; see above.

> but for some reasons am
> not satisfied with the outcome. Why? Strange...

Come on, you know very well that the outcome using redispatching is 
quite different from the outcome without redispatching. So of course my 
reason is that I want the former outcome :-)

The same outcome could, of course, be gained in another design that does 
not use redispatching, but at the cost of a different architecture, 
which (in my view) had significant disadvantages such as code duplication.

I suggest that you show how you would design the Divide-and-Conquer 
example that I described in my reply to Randy earlier today, and then we 
can compare the two designs.

>>> that re-dispatch requires referential semantics,
>> I don't see why,
> 
> Because of the type tag. You need same representation for T and T'Class to
> have T -> T'Class as a view conversion.

That does not imply reference *semantics*, only pass by reference. Not 
the same thing, as demonstrated by pure functional languages.

>> but I don't really care, as I have accepted (albeit 
>> reluctantly) that I need referential semantics anyway.
> 
> But it means that the design relies on referential semantics and cannot be
> rewritten in terms of value semantics.

Redispatch does *not* make the design rely on referential semantics. I 
admit that my design(s) do rely on referential semantics in places, but 
this is not because of redispatch.

> Formally there should be something
> fundamentally wrong with that, because there is no reason beyond 1.
> optimization issues, 2. outer world communication, why referential
> semantics is chosen. Yet we rely on it. Why?

In my case, mainly for reason 1. optimization issues. Also because it 
can sometimes reduce architectural coupling between operations and packages.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-14 16:03                                 ` Niklas Holsti
@ 2009-08-15  9:47                                   ` Dmitry A. Kazakov
  2009-08-15 19:19                                     ` Niklas Holsti
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-15  9:47 UTC (permalink / raw)


On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote:

> Or we don't agree on the formalism, which seems to be the case for you 
> and me.

Once people agreed on a formalism there would be no place for
discussions... (;-))
 
>>> If your argument is just that a program with redispatching is more 
>>> complex and thus more likely to contain programming errors, I partly 
>>> agree; redispatching makes the sequence of invoked operations more 
>>> dynamic and thus possibly harder to grasp and make correct. But this 
>>> argument applies as well to any kind of run-time dispatching, not just 
>>> to *re*-dispatching.
>> 
>> I disagree. Dispatching does not make it more complex assuming that all
>> implementations of the primitive operation are conform the interface. You
>> write the client program in terms of another type, of the type T'Class.
>> There is nothing automatically more complex in T'Class than in T. 
> 
> If you assume that all operation implementations "conform to the 
> interface" (including semantics) then redispatching is no more complex 
> than dispatching; the effect of the (re)dispatching call is assumed to 
> be the desired one, whatever the actual type of the controlling operand(s).

LSP stuff, huh. No. T already conforms to T'Class, so why do you
re-dispatch? The answer is that semantically it does not conform, but the
thing you are dispatching to does. That is the complexity. You rely on
different behavior of T'Class and T. But according to the interface,
T'Class and T are interchangeable. So have broken that convention.

>> When you call an operation of the type S on T, that
>> is a type error to me. You have circumvented the type system, by an
>> explicit type conversion of T to T'Class. Do not we agree that type
>> conversions should be avoided?
> 
> I agree that type conversions "should" be avoided, but good grief, 
> sometimes you need them to express something, as in this case,

That is the point. What does it express at the semantic level? Why should
it be expressed in this form?

> because 
> Ada specifically uses the conversion T->T'Class to invoke dynamic 
> (re)dispatching. And of course this conversion is safe and does not in 
> any way "circumvent" the type system.

That depends on definitions. Address to access conversion is also "safe" in
some sense. The types involved in a conversion are formally different,
which implies different semantics. Once you change the semantics, it is
automatically unsafe.

>> Why do you dispatch again, you already checked the tag.
> 
> Because this gives me the functionality I want, of course.

That is clear, but if you say that the functionality you wanted is
re-dispatch itself, that could not justify it. There must be something else
in it, which raises the question of design. You have implemented something
using re-dispatch. My position is that it is a bad way to implement
anything. You disagree... (:-))

>> The type is determined,
> 
> Of course not, only the point (within the class) of the implementation 
> of the currently executing, (possibly) inherited operation (the caller) 
> is determined. The actual type, as you well know, is any type in 
> T'Class, although it is written "T" in the operation profile.

No, the actual type is T, just because the operation declaration tells so.

If you are in the operation inherited by S from T and you don't want it to
be T there, then why do have you inherited and not overridden it in S?
Something must be wrong in it.

>> the reader expects the code to correspond that type,
>> you break this convention.
> 
> That depends on the "reader". My convention is different from yours, and 
> I think my convention corresponds more closely to the (full) Ada 
> language, while yours assumes a subset of Ada that follows your design 
> rules.

Are you saying that the reader must be constantly aware that the type of
the actual parameter could be not the type the procedure deals with?

>> My concern is the semantics of the code.
>> Semantically a primitive operation has separate bodies for each type in the
>> class.
> 
> Yes, but they are represented by a smaller (sparse) number of bodies in 
> the program text, with the "missing" bodies supplied by inheritance. And 
> that is why the actual type of the actual parameter object is some type 
> in T'Class, not always T (as you well know...)

False inheritance cannot serve an argument, see above. You broke it when
inherited where you should have overridden. Then are trying to mend things
in some third place...

>> This gets broken in your design. I.e. it is a third way of how
>> bodies are decomposed. I may agree that the language could offer more than
>> one body per class (class-wide) vs. one body per type (primitive). But so
>> far, a design should conform to the existing model.
> 
> I fail to understand that. You seem to be claiming that the Ada "model" 
> does not really include redispatching, which is nonsense to me.

No it refers to other models of polymorphic operations, like extensible
bodies or the thing I described below.

>> but for some reasons am
>> not satisfied with the outcome. Why? Strange...
> 
> Come on, you know very well that the outcome using redispatching is 
> quite different from the outcome without redispatching. So of course my 
> reason is that I want the former outcome :-)

Yes, this is the core question. You don't want the semantics of the type T
being in an operation of.

> The same outcome could, of course, be gained in another design that does 
> not use redispatching, but at the cost of a different architecture, 
> which (in my view) had significant disadvantages such as code duplication.
> 
> I suggest that you show how you would design the Divide-and-Conquer 
> example that I described in my reply to Randy earlier today, and then we 
> can compare the two designs.

   type Subproblem is abstract ...;
   procedure Conquer (X : in out Subproblem) is abstract;
   type List_Of_Subproblems is ...; -- Container type of Subproblem'Class

   type Problem is abstract ...;
   function Divide (X : Problem) return List_Of_Subproblems is abstract;

A better case for re-dispatch is an operation defined in terms of other
operations. For example x*n defined as x+...+x, n times. you want to have a
standard version of *, but in some cases to have an ability to implement it
specifically.

I am using a sort of this:

   procedure Add (X : in out Item; Y : Item);
   procedure Mul (X : in out Item'Class; N : Positive);
private
   function Has_Accelerated_Mul (X : Item) return Boolean;
   procedure Accelerated_Mul (X : in out Item; N : Positive);

   procedure Mul (X : in out Item'Class; N : Positive) is
   begin
      if Has_Accelerated_Mul (X) then
         Accelerated_Mult (X, N);
      else
         ... -- Do it slowly
      end if;
   end Mul;

I don't like it, because it exposes Mul as class-wide, while Add is
primitive. It would be nice if Ada has supported a kind of decomposition,
with a fall back to a class-wide operation.

My empirical condition for all dispatching operations is that type tag
resolution should move strictly towards the root, and never backwards. I.e.
if you have a tag for T'Class,, you can narrow the set of candidates
ultimately to the single type, but you never widen the set.

>>>> that re-dispatch requires referential semantics,
>>> I don't see why,
>> 
>> Because of the type tag. You need same representation for T and T'Class to
>> have T -> T'Class as a view conversion.
> 
> That does not imply reference *semantics*, only pass by reference.

Formal argument is a view of the actual one. No difference.

If Ada had classes of by-value types, then Integer'Class would have a
different representation, and Integer -> Integer'Class would be a full
scale conversion producing a new object. Consequently there would no way to
re-dispatch from Float converted to Integer (in case they were in the same
hierarchy of types).

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



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

* Re: Access types as parameters
  2009-08-15  9:47                                   ` Dmitry A. Kazakov
@ 2009-08-15 19:19                                     ` Niklas Holsti
  2009-08-16  8:32                                       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-15 19:19 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote:
> 
>> Or we don't agree on the formalism, which seems to be the case for you 
>> and me.
> 
> Once people agreed on a formalism there would be no place for
> discussions... (;-))

Perhaps in the ideal, yes. But formalisms in "design" are not yet that 
powerful or unambiguous :-)

>>> Why do you dispatch again, you already checked the tag.
>> Because this gives me the functionality I want, of course.
> 
> That is clear, but if you say that the functionality you wanted is
> re-dispatch itself, that could not justify it. There must be something else
> in it, which raises the question of design. You have implemented something
> using re-dispatch. My position is that it is a bad way to implement
> anything. You disagree... (:-))

Well, you still haven't shown me any convincing reason for calling it a 
bad design, because I don't accept your "type error" argument.

>>> The type is determined,
>> Of course not, only the point (within the class) of the implementation 
>> of the currently executing, (possibly) inherited operation (the caller) 
>> is determined. The actual type, as you well know, is any type in 
>> T'Class, although it is written "T" in the operation profile.
> 
> No, the actual type is T, just because the operation declaration tells so.

This seems to be the origin of our disagreement. You want to view the 
object as of type T, although at run-time it may be a T-view of an 
object of a derived type S. This means that you cannot redispatch. But 
this does not entitle you to call redispatching "bad" in general.

> If you are in the operation inherited by S from T and you don't want it to
> be T there, then why do have you inherited and not overridden it in S?
> Something must be wrong in it.

Not at all, it works perfectly for S. If I had overridden it for S, it 
would have exactly the same text as for T (except of course for the 
change in type name from T to S).

If the operation did *not* use redispatching, it would *not* work for T 
as well as for S, and I would have to override it for S.

> Are you saying that the reader must be constantly aware that the type of
> the actual parameter could be not the type the procedure deals with?

Yes, of course. Because of inheritance.

>> I suggest that you show how you would design the Divide-and-Conquer 
>> example that I described in my reply to Randy earlier today, and then we 
>> can compare the two designs.
> 
>    type Subproblem is abstract ...;
>    procedure Conquer (X : in out Subproblem) is abstract;
>    type List_Of_Subproblems is ...; -- Container type of Subproblem'Class
> 
>    type Problem is abstract ...;
>    function Divide (X : Problem) return List_Of_Subproblems is abstract;

Ok, good for discussing. This works, but has some drawbacks compared to 
a redispatching solution. Firstly, it needs new data structures 
(Subproblem and List_Of_Subproblems) which may require dynamic memory 
allocation if the Subproblem type is complex. Secondly, it is no longer 
possible to stop the Divide operation when the first solvable Subproblem 
is discovered. Thirdly, you now need a class-wide operation that invokes 
Divide and then Conquer for the given object of type T'Class.

Finally, a point that is not a drawback in my view, but should be in 
your view (as I understand it): there is still no guarantee that the 
actual invoked Divide operation and the actual invoked Conquer operation 
are defined for the same type; the actual T'Class object might inherit 
Divide from type T, and Conquer from type S.

Logically (but perhaps exaggerating a bit) you should consider this a 
type error because it is mixing the semantics of T and S. I'm sure you 
don't see this as a problem, probably because you assume that the 
semantics of T and S are sufficiently "compatible", being all in 
T'Class. But this is just the reason why I don't think that 
redispatching from Divide (T) to Conquer (S) is a type error.

> A better case for re-dispatch is an operation defined in terms
> of other operations.

Well, I do think that Divide, in my example, is defined in terms of 
other operations, namely Conquer, because Divide (in my design) finds 
the subproblems and calls Conquer on each subproblem. Perhaps I should 
have called the operation Divide_And_Conquer to make this clear in the 
name, sorry.

> For example x*n defined as x+...+x, n times. you want to have a
> standard version of *, but in some cases to have an ability to
> implement it specifically.

I think that is quite similar to the Divide/Conquer example.

> I am using a sort of this:
> 
>    procedure Add (X : in out Item; Y : Item);
>    procedure Mul (X : in out Item'Class; N : Positive);
> private
>    function Has_Accelerated_Mul (X : Item) return Boolean;
>    procedure Accelerated_Mul (X : in out Item; N : Positive);
> 
>    procedure Mul (X : in out Item'Class; N : Positive) is
>    begin
>       if Has_Accelerated_Mul (X) then
>          Accelerated_Mult (X, N);

Shudder (my turn :-). This is a kind of manually implemented overriding, 
where Accelerated_Mul sometimes "overrides" Mul. I much prefer to use 
the language-defined overriding of primitive operations, so I would make 
Mul primitive on Item, instead of class-wide, and override it with an 
accelerated version for those Item'Class types that allow it. This is 
probably faster, too :-)

One problem in your design is that someone can now mistakenly call 
Accelerated_Mul (X) even if Has_Accelerated_Mul (X) is False. Moreover, 
Has_Accelerated_Mul seems to depend on the particular object (X), not 
just on the type (Item). Or was this your intention?

> My empirical condition for all dispatching operations is that type tag
> resolution should move strictly towards the root, and never backwards. I.e.
> if you have a tag for T'Class,, you can narrow the set of candidates
> ultimately to the single type, but you never widen the set.

This rule defines a subset of Ada that you want to use. OK, but so far 
you have not convinced me that there is any benefit in this subset.

We are all struggling to find the small nuggets of correct programs 
hidden in the vast rockpile of incorrect programs. I can believe that 
your empirical condition is helpful to you, but I have not found any 
problems with redispatching.

I suggest that we agree to disagree and close here.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-15 19:19                                     ` Niklas Holsti
@ 2009-08-16  8:32                                       ` Dmitry A. Kazakov
  2009-08-16  9:52                                         ` Niklas Holsti
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-16  8:32 UTC (permalink / raw)


On Sat, 15 Aug 2009 22:19:41 +0300, Niklas Holsti wrote:

> Dmitry A. Kazakov wrote:
>> On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote:
>> 
>>> Of course not, only the point (within the class) of the implementation 
>>> of the currently executing, (possibly) inherited operation (the caller) 
>>> is determined. The actual type, as you well know, is any type in 
>>> T'Class, although it is written "T" in the operation profile.
>> 
>> No, the actual type is T, just because the operation declaration tells so.
> 
> This seems to be the origin of our disagreement. You want to view the 
> object as of type T, although at run-time it may be a T-view of an 
> object of a derived type S. This means that you cannot redispatch. But 
> this does not entitle you to call redispatching "bad" in general.

Ada does not allow other cases, I mean dispatching while preserving a
class-wide view on the object. What comes in mind:

1. procedure Foo (X : T'Class) is
   begin
       if X in S'Class then
         Bar (X); -- Now "dispatch" again
       elsif X in Q'Class then
         Baz (X); -- "dispatch" again

This is a form of re-dispatch since the tag of X is analyzed twice.

2. procedure Foo (X1 : T; X2 : T'Class);

called as

   Foo (X, X);

Neither really represent a good design pattern.

>> If you are in the operation inherited by S from T and you don't want it to
>> be T there, then why do have you inherited and not overridden it in S?
>> Something must be wrong in it.
> 
> Not at all, it works perfectly for S. If I had overridden it for S, it 
> would have exactly the same text as for T (except of course for the 
> change in type name from T to S).

That is a type error in Ada: you cannot call an operation of T on S,
without a type conversion. Once you converted S to T, it is no more S. It
is an ugly hack that allows you to restore S by casting it to T'Class. It
is also a burden for the implementation.

> If the operation did *not* use redispatching, it would *not* work for T 
> as well as for S, and I would have to override it for S.

Yes, this is why S is not substitutable in the operations of T, you shall
override it.

The semantic problem is that you want to inherit "text", while substituting
type annotations. This is not the model of inheritance. The proper models
for this are class-wide operations and generic bodies.

>> Are you saying that the reader must be constantly aware that the type of
>> the actual parameter could be not the type the procedure deals with?
> 
> Yes, of course. Because of inheritance.

Shudder again.

>> For example x*n defined as x+...+x, n times. you want to have a
>> standard version of *, but in some cases to have an ability to
>> implement it specifically.
> 
> I think that is quite similar to the Divide/Conquer example.
> 
>> I am using a sort of this:
>> 
>>    procedure Add (X : in out Item; Y : Item);
>>    procedure Mul (X : in out Item'Class; N : Positive);
>> private
>>    function Has_Accelerated_Mul (X : Item) return Boolean;
>>    procedure Accelerated_Mul (X : in out Item; N : Positive);
>> 
>>    procedure Mul (X : in out Item'Class; N : Positive) is
>>    begin
>>       if Has_Accelerated_Mul (X) then
>>          Accelerated_Mult (X, N);
> 
> Shudder (my turn :-). This is a kind of manually implemented overriding, 
> where Accelerated_Mul sometimes "overrides" Mul. I much prefer to use 
> the language-defined overriding of primitive operations, so I would make 
> Mul primitive on Item, instead of class-wide, and override it with an 
> accelerated version for those Item'Class types that allow it. This is 
> probably faster, too :-)

There is no mechanism for it. Re-dispatch is a hack. What do you do is
semantically not inheritance but overriding with an instance of a generic
body, or some class-wide body. I think this is the key issue. 

Considering this semantics, there is no necessity to sink the type tag down
to T only to later raise it back to S. It should dispatch to S, not to T.
So re-dispatch is still a hack that indicates a language problem here. I am
using another hack in comparable situations: the Rosen trick. At least it
makes explicit that each object "knows" itself.

> One problem in your design is that someone can now mistakenly call 
> Accelerated_Mul (X) even if Has_Accelerated_Mul (X) is False.

Accelerated_Mul is private. But you could merge it with
Has_Accelerated_Mul.

> Moreover, 
> Has_Accelerated_Mul seems to depend on the particular object (X), not 
> just on the type (Item). Or was this your intention?

We cannot dispatch on bare tags in Ada, so I am forced to use object where
the type tag would suffice.

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



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

* Re: Access types as parameters
  2009-08-16  8:32                                       ` Dmitry A. Kazakov
@ 2009-08-16  9:52                                         ` Niklas Holsti
  2009-08-16 12:38                                           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-16  9:52 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Sat, 15 Aug 2009 22:19:41 +0300, Niklas Holsti wrote:
> 
>> Dmitry A. Kazakov wrote:
>>> On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote:
>>>
>>>> Of course not, only the point (within the class) of the implementation 
>>>> of the currently executing, (possibly) inherited operation (the caller) 
>>>> is determined. The actual type, as you well know, is any type in 
>>>> T'Class, although it is written "T" in the operation profile.
>>> No, the actual type is T, just because the operation declaration tells so.
>> This seems to be the origin of our disagreement. You want to view the 
>> object as of type T, although at run-time it may be a T-view of an 
>> object of a derived type S. This means that you cannot redispatch. But 
>> this does not entitle you to call redispatching "bad" in general.
> 
> Ada does not allow other cases, I mean dispatching while preserving a
> class-wide view on the object. What comes in mind:
> 
> 1. procedure Foo (X : T'Class) is
>    begin
>        if X in S'Class then
>          Bar (X); -- Now "dispatch" again
>        elsif X in Q'Class then
>          Baz (X); -- "dispatch" again
> 
> This is a form of re-dispatch since the tag of X is analyzed twice.

I don't see how this relates to our subject. Yes, that code looks at the 
tag of X. Are you saying that any inspection of the tag of an object is 
"bad design", except for the case of "basic" dispatching?

Although I sometimes do it, I don't much like to inspect tags, as in Foo 
above, because it couples the logic of a class-wide operation to the 
existence and properties of certain explicitly named sub-classes, which 
is fragile. But a redispatching call is not fragile in this way.

> Re-dispatch is a hack. What do you do is
> semantically not inheritance but overriding with an instance of a generic
> body, or some class-wide body. I think this is the key issue. 

I'm sorry, I don't understand why it is "semantically not inheritance". 
If this is again the "type error" issue, I still don't agree with you. 
Your other arguments use terms and concepts that are too vague (for me) 
to be argued about, so I don't see how we can progress usefully. Thanks 
for the discussion.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-16  9:52                                         ` Niklas Holsti
@ 2009-08-16 12:38                                           ` Dmitry A. Kazakov
  2009-08-16 13:21                                             ` Niklas Holsti
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-16 12:38 UTC (permalink / raw)


On Sun, 16 Aug 2009 12:52:53 +0300, Niklas Holsti wrote:

> Dmitry A. Kazakov wrote:
>> On Sat, 15 Aug 2009 22:19:41 +0300, Niklas Holsti wrote:
>> 
>>> Dmitry A. Kazakov wrote:
>>>> On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote:
>>>>
>>>>> Of course not, only the point (within the class) of the implementation 
>>>>> of the currently executing, (possibly) inherited operation (the caller) 
>>>>> is determined. The actual type, as you well know, is any type in 
>>>>> T'Class, although it is written "T" in the operation profile.
>>>> No, the actual type is T, just because the operation declaration tells so.
>>> This seems to be the origin of our disagreement. You want to view the 
>>> object as of type T, although at run-time it may be a T-view of an 
>>> object of a derived type S. This means that you cannot redispatch. But 
>>> this does not entitle you to call redispatching "bad" in general.
>> 
>> Ada does not allow other cases, I mean dispatching while preserving a
>> class-wide view on the object. What comes in mind:
>> 
>> 1. procedure Foo (X : T'Class) is
>>    begin
>>        if X in S'Class then
>>          Bar (X); -- Now "dispatch" again
>>        elsif X in Q'Class then
>>          Baz (X); -- "dispatch" again
>> 
>> This is a form of re-dispatch since the tag of X is analyzed twice.
> 
> I don't see how this relates to our subject.

Just an example of how type tag is dealt with while keeping a class-wide
view of the object. Re-dispatching is no different. You publicly check the
tag, declare the result of T, but keep in the sleeve S. Let's play by
rules... (:-))

> Yes, that code looks at the 
> tag of X. Are you saying that any inspection of the tag of an object is 
> "bad design", except for the case of "basic" dispatching?

With a high degree of probability it is. In all cases I have to check tag I
feel myself guilty.

> Although I sometimes do it, I don't much like to inspect tags, as in Foo 
> above, because it couples the logic of a class-wide operation to the 
> existence and properties of certain explicitly named sub-classes, which 
> is fragile. But a redispatching call is not fragile in this way.

It is fragile because it has a behavior that is not determined by the
values attributed to the type T. Its behavior is determined by the values
of T'Class, which is an open-ended set.

>> Re-dispatch is a hack. What do you do is
>> semantically not inheritance but overriding with an instance of a generic
>> body, or some class-wide body. I think this is the key issue. 
> 
> I'm sorry, I don't understand why it is "semantically not inheritance". 

Type S inherits F from T by composition of T.F with a type conversion:

   S.F = T.F o S_from_T

That is the only way to define it in a typed language where S and T are
distinct types. This is also how Ada defines it. Any primitive operation
gets "re-declared" when you derive S from T.

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



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

* Re: Access types as parameters
  2009-08-16 12:38                                           ` Dmitry A. Kazakov
@ 2009-08-16 13:21                                             ` Niklas Holsti
  2009-08-16 17:58                                               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: Niklas Holsti @ 2009-08-16 13:21 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> On Sun, 16 Aug 2009 12:52:53 +0300, Niklas Holsti wrote:
> 
>> Are you saying that any inspection of the tag of an object is 
>> "bad design", except for the case of "basic" dispatching?
> 
> With a high degree of probability it is. In all cases I have to check tag I
> feel myself guilty.

Me too. But it is sometimes the best solution (in Ada 95 at least), 
although it may cause maintenance problems.

>> Although I sometimes do it, I don't much like to inspect tags, as in Foo 
>> above, because it couples the logic of a class-wide operation to the 
>> existence and properties of certain explicitly named sub-classes, which 
>> is fragile. But a redispatching call is not fragile in this way.
> 
> It is fragile because it has a behavior that is not determined by the
> values attributed to the type T. Its behavior is determined by the values
> of T'Class, which is an open-ended set.

No. That argument makes *any* class-wide operation (using dispatching) 
"fragile", because "its behaviour is determined by the open-ended set" 
of operation implementations. We must accept this kind of "fragility" if 
we use dispatching at all.

I persist in seeing no extra fragility in redispatching.

>>> Re-dispatch is a hack. What do you do is
>>> semantically not inheritance but overriding with an instance of a generic
>>> body, or some class-wide body. I think this is the key issue. 
>> I'm sorry, I don't understand why it is "semantically not inheritance". 
> 
> Type S inherits F from T by composition of T.F with a type conversion:
> 
>    S.F = T.F o S_from_T

I think you have the conversion the wrong way around: If S.F should 
apply to an object X of type S, then X must first be converted to T, and 
then T.F applied:

    S.F = T.F o T_from_S

meaning that

    S.F (X) = T.F (T_from_S (X))

> That is the only way to define it in a typed language where S and T are
> distinct types. This is also how Ada defines it. Any primitive operation
> gets "re-declared" when you derive S from T.

Yes, yes, but Ada and other OO languages preserve the ability to 
redispatch, which does not fit into this simple mathematical 
functional-composition formalism. But then, we agreed that we use 
different formalisms, so all is OK.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



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

* Re: Access types as parameters
  2009-08-16 13:21                                             ` Niklas Holsti
@ 2009-08-16 17:58                                               ` Dmitry A. Kazakov
  0 siblings, 0 replies; 31+ messages in thread
From: Dmitry A. Kazakov @ 2009-08-16 17:58 UTC (permalink / raw)


On Sun, 16 Aug 2009 16:21:11 +0300, Niklas Holsti wrote:

> Dmitry A. Kazakov wrote:
>> On Sun, 16 Aug 2009 12:52:53 +0300, Niklas Holsti wrote:
>> 
>>> Although I sometimes do it, I don't much like to inspect tags, as in Foo 
>>> above, because it couples the logic of a class-wide operation to the 
>>> existence and properties of certain explicitly named sub-classes, which 
>>> is fragile. But a redispatching call is not fragile in this way.
>> 
>> It is fragile because it has a behavior that is not determined by the
>> values attributed to the type T. Its behavior is determined by the values
>> of T'Class, which is an open-ended set.
> 
> No. That argument makes *any* class-wide operation (using dispatching) 
> "fragile", because "its behaviour is determined by the open-ended set" 
> of operation implementations. We must accept this kind of "fragility" if 
> we use dispatching at all.

The fragility is not in the set size, but in the operation that handles it
implicitly. It is like with side effects. You do one thing, but rely on
more than that. Dispatching on the context of the type T it is fragile. On
the context of T'Class it is not.

>>>> Re-dispatch is a hack. What do you do is
>>>> semantically not inheritance but overriding with an instance of a generic
>>>> body, or some class-wide body. I think this is the key issue. 
>>> I'm sorry, I don't understand why it is "semantically not inheritance". 
>> 
>> Type S inherits F from T by composition of T.F with a type conversion:
>> 
>>    S.F = T.F o S_from_T
> 
> I think you have the conversion the wrong way around: If S.F should 
> apply to an object X of type S, then X must first be converted to T, and 
> then T.F applied:
> 
>     S.F = T.F o T_from_S

Yes.

> meaning that
> 
>     S.F (X) = T.F (T_from_S (X))

Right.

>> That is the only way to define it in a typed language where S and T are
>> distinct types. This is also how Ada defines it. Any primitive operation
>> gets "re-declared" when you derive S from T.
> 
> Yes, yes, but Ada and other OO languages preserve the ability to 
> redispatch, which does not fit into this simple mathematical 
> functional-composition formalism. But then, we agreed that we use 
> different formalisms, so all is OK.

Simple or complex I didn't see different formalisms, that don't fall apart
when classes of built-in types come into consideration. Everything is
reference? Is reference a reference? etc. And even if re-dispatch could be
reconciled with strog typing, an aftertaste remains, to do same thing twice
looks wrong.

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



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

* Re: Access types as parameters
  2009-08-13  1:06                   ` Randy Brukardt
  2009-08-13  8:34                     ` Niklas Holsti
@ 2009-08-18 12:22                     ` Stephen Leake
  1 sibling, 0 replies; 31+ messages in thread
From: Stephen Leake @ 2009-08-18 12:22 UTC (permalink / raw)


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

> "Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message 
> news:uprb1epp8.fsf@stephe-leake.org...
>
>> To be convincing, I'd have to post an example from Gtk or OpenToken
>> written both ways; maybe later I'll find time for that.
>
> Yeah, I know that we are both talking rather hypothetically, and we may have 
> rather different examples in mind.

I've now written a small example of the two styles; see
http://www.stephe-leake.org/ada/access_vs_object.html

The problem domain of this example is grammars; the goal of the
application is to allow the user to build a data structure that
represents a grammar, so the application can then use that structure
to parse input strings.

Typical grammars have recursion in them. This requires pointers of
some form in the data structure. The access_style package tree in this
example uses explicit access types as the pointers, and access
parameters in the relevant grammar constructing functions. The
object_style package tree uses Ada.Containers Cursors as the pointers.

The object style contains a significant kludge; there is a global (but
private, in Object_Style.Pointers spec) List object that holds all the
tokens needed for the grammar. That was the only way I could see to
get a Cursor for each. AI05-0069-1
(http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0069-1.txt)
defines holder containers that should eliminate this kludge in Ada
201z.

In addition, in Object_Style.Sequence "&" body, we must examine the
tag of the arguments, rather than using dispatching to achieve the
same result. This doesn't happen in the access style, because we can
define two "&" functions with different type profiles; one takes a
token object, the other an access to class-wide.

Finally, some type information is lost with this style. Compare
object_style-run.adb to access_style-run.adb; there are more precise
types in the access style version. In the full OpenToken application,
this becomes even more significant.

There are two main objections to the access style:

1) Users must manage memory
  
2) If users pass in access values with nested accessibility, they will
   get Program_Error at run-time.

The first objection can be mitigated by realizing that there is never
any need (in this application) to deallocate any of the grammar
structures. This could be made explicit with a new pragma
No_Deallocation, applied to the access types.

The second objection can be eliminated by adding preconditions to the
subprograms that take access parameters, as defined by ai05-0145-1 and
ai05-0024-1:

pragma Inherited_Precondition ("&", Left in Access_Style.Token_Access_Type);

A reasonably smart compiler can then report the accessibility errors
at compile time rather than runtime.

A third style would be a modification of the access style; define the
access type storage_size to be zero, so that no allocations (or
deallocations) can be done. This forces the user to define aliased
grammar objects on the stack at library level, and then reference them
in other grammar objects via 'access. That is significantly more
awkward than allowing allocation, but not deallocation.

With the facilities of Ada 201z, I believe using explicit access types
and access parameters is the best fit to the problem domain. Using
container Cursors as the pointers is awkward, loses type information,
and doesn't gain enough to be worth it.

This thread originally started discussing Gtk, which also uses access
parameters. In my (few, small) Gtk applications, I don't deallocate
Gtk widgets; I create them all at the start, and then show and hide
them as appropriate. So I could live with a Gtk that had pragma
No_Deallocations on the access types. Other people might object to
that, though :).

-- 
-- Stephe



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

end of thread, other threads:[~2009-08-18 12:22 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-07-17  8:39 Access types as parameters Rick
2009-07-17 15:03 ` Adam Beneschan
2009-07-17 16:28   ` Hibou57 (Yannick Duchêne)
2009-07-17 23:25     ` rickduley
2009-07-18  1:03       ` Randy Brukardt
2009-07-19 22:57         ` rickduley
2009-07-20  0:10           ` John B. Matthews
2009-07-20  8:13           ` Dmitry A. Kazakov
2009-07-21  0:34           ` Randy Brukardt
2009-07-21 14:34           ` Adam Beneschan
2009-07-23  2:11             ` Stephen Leake
2009-08-11 23:41               ` Randy Brukardt
2009-08-12  2:22                 ` Stephen Leake
2009-08-13  1:06                   ` Randy Brukardt
2009-08-13  8:34                     ` Niklas Holsti
2009-08-13  9:15                       ` Dmitry A. Kazakov
2009-08-13 20:13                         ` Niklas Holsti
2009-08-13 21:07                           ` Dmitry A. Kazakov
2009-08-14  9:27                             ` Niklas Holsti
2009-08-14 10:36                               ` Dmitry A. Kazakov
2009-08-14 16:03                                 ` Niklas Holsti
2009-08-15  9:47                                   ` Dmitry A. Kazakov
2009-08-15 19:19                                     ` Niklas Holsti
2009-08-16  8:32                                       ` Dmitry A. Kazakov
2009-08-16  9:52                                         ` Niklas Holsti
2009-08-16 12:38                                           ` Dmitry A. Kazakov
2009-08-16 13:21                                             ` Niklas Holsti
2009-08-16 17:58                                               ` Dmitry A. Kazakov
2009-08-14  4:07                       ` Randy Brukardt
2009-08-14 10:22                         ` Niklas Holsti
2009-08-18 12:22                     ` Stephen Leake

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