Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Access types as parameters

60 views
Skip to first unread message

Rick

unread,
Jul 17, 2009, 4:39:45 AM7/17/09
to
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;

Adam Beneschan

unread,
Jul 17, 2009, 11:03:29 AM7/17/09
to

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

Hibou57 (Yannick Duchêne)

unread,
Jul 17, 2009, 12:28:11 PM7/17/09
to
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.

rickduley

unread,
Jul 17, 2009, 7:25:10 PM7/17/09
to rick...@gmail.com
On Jul 18, 12:28 am, Hibou57 (Yannick Duchêne)

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?

Randy Brukardt

unread,
Jul 17, 2009, 9:03:12 PM7/17/09
to
"rickduley" <rick...@gmail.com> wrote in message
news:8410fc60-9b8a-4f82...@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.


rickduley

unread,
Jul 19, 2009, 6:57:12 PM7/19/09
to
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);

John B. Matthews

unread,
Jul 19, 2009, 8:10:01 PM7/19/09
to
In article
<e325296c-7114-4540...@f18g2000prf.googlegroups.com>,
rickduley <rick...@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>

Dmitry A. Kazakov

unread,
Jul 20, 2009, 4:13:48 AM7/20/09
to
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

Randy Brukardt

unread,
Jul 20, 2009, 8:34:56 PM7/20/09
to
"rickduley" <rick...@gmail.com> wrote in message
news:e325296c-7114-4540...@f18g2000prf.googlegroups.com...

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.


Adam Beneschan

unread,
Jul 21, 2009, 10:34:53 AM7/21/09
to

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

Stephen Leake

unread,
Jul 22, 2009, 10:11:29 PM7/22/09
to
Adam Beneschan <ad...@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

Randy Brukardt

unread,
Aug 11, 2009, 7:41:21 PM8/11/09
to
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" <stephe...@stephe-leake.org> wrote in message
news:u63dkb...@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.


Stephen Leake

unread,
Aug 11, 2009, 10:22:27 PM8/11/09
to
"Randy Brukardt" <ra...@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" <stephe...@stephe-leake.org> wrote in message
> news:u63dkb...@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.

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

Randy Brukardt

unread,
Aug 12, 2009, 9:06:23 PM8/12/09
to
"Stephen Leake" <stephe...@stephe-leake.org> wrote in message
news:uprb1e...@stephe-leake.org...

> "Randy Brukardt" <ra...@rrsoftware.com> writes:
>> "Stephen Leake" <stephe...@stephe-leake.org> wrote in message
>> news:u63dkb...@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.


Niklas Holsti

unread,
Aug 13, 2009, 4:34:20 AM8/13/09
to
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
. @ .

Dmitry A. Kazakov

unread,
Aug 13, 2009, 5:15:43 AM8/13/09
to
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.

Niklas Holsti

unread,
Aug 13, 2009, 4:13:39 PM8/13/09
to
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.

Dmitry A. Kazakov

unread,
Aug 13, 2009, 5:07:54 PM8/13/09
to
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.

Randy Brukardt

unread,
Aug 14, 2009, 12:07:47 AM8/14/09
to
"Niklas Holsti" <niklas...@tidorum.invalid> wrote in message
news:4a83d018$0$26303$4f79...@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.

Niklas Holsti

unread,
Aug 14, 2009, 5:27:02 AM8/14/09
to
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

unread,
Aug 14, 2009, 6:22:38 AM8/14/09
to
Randy Brukardt wrote:
> "Niklas Holsti" <niklas...@tidorum.invalid> wrote in message
> news:4a83d018$0$26303$4f79...@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.

Dmitry A. Kazakov

unread,
Aug 14, 2009, 6:36:14 AM8/14/09
to

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?

Niklas Holsti

unread,
Aug 14, 2009, 12:03:52 PM8/14/09
to

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.

Dmitry A. Kazakov

unread,
Aug 15, 2009, 5:47:52 AM8/15/09
to
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).

Niklas Holsti

unread,
Aug 15, 2009, 3:19:41 PM8/15/09
to
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.

Dmitry A. Kazakov

unread,
Aug 16, 2009, 4:32:45 AM8/16/09
to
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.

Niklas Holsti

unread,
Aug 16, 2009, 5:52:53 AM8/16/09
to
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.

Dmitry A. Kazakov

unread,
Aug 16, 2009, 8:38:00 AM8/16/09
to
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.

Niklas Holsti

unread,
Aug 16, 2009, 9:21:11 AM8/16/09
to
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.

Dmitry A. Kazakov

unread,
Aug 16, 2009, 1:58:12 PM8/16/09
to
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.

Stephen Leake

unread,
Aug 18, 2009, 8:22:30 AM8/18/09
to
"Randy Brukardt" <ra...@rrsoftware.com> writes:

> "Stephen Leake" <stephe...@stephe-leake.org> wrote in message

> news:uprb1e...@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

0 new messages