From: Dmitry A. Kazakov on
On Fri, 20 Nov 2009 15:56:50 +0200, Niklas Holsti wrote:

> Dmitry has philosophical objections to redispatching, which is why he
> suggests that you should declare P with a class-type parameter THIS in
> the first place, so that A (THIS) will dispatch.

I would not call them philosophical. If you see the code like

if X = 1 then
...
if X = 1 then
...
end if;
...
if X = 1 then
...
end if;
...

you start suggesting that there could be something wrong here. Re-dispatch
is not an error it is an indication of a potential design problem. Why
would you need to check twice the same tag? That requires an explanation in
the design document and comments in the code.

A technical issue is that if we had tagged types of value semantics (i.e.
classes rooted in Boolean, Integer etc), then re-dispatch could not work
with them. I.e. from Ada's agnostic point of view on by-value vs.
by-reference, re-dispatch (or speaking more generally, view conversion to
T'Class) is an implementation-specific kludge.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Ludovic Brenta on
I'd like to follow up on the (correct) replies in this thread so far
with a "dynamic dispatching in Ada for C++ programmers" primer.

In C++:

class C {
virtual void foo ();
}
C object;
C* pointer = &object;

void p () {
object.foo (); // static dispatch
pointer->foo (); // dynamic dispatch
}

In C++, class types are specific and pointer types are class-wide. The
declaration

C* pointer = &object;

is strictly equivalent to

Object : aliased C;
type C_Access is access all C'Class;
Pointer : C_Access := Object'Access;

Ada makes it explicit that Pointer is of a class-wide type, therefore
calls through the pointer dispatch dynamically:

procedure P is
begin
Foo (Object); -- static dispatch
Foo (Pointer.all); -- dynamic dispatch
end P;

So far it seems that Ada and C++ are really the same, but wait! Ada
has a syntax to declare access types to a specific type, like so:

type C_Specific_Access is access all C;

Any calls to primitive operations of C through C_Specific_Access will
dispatch statically, not dynamically. There is no way in C++ to
declare such a type. In C++, all pointer types are class-wide; this
applies also to the type of the implicit "this" parameter.

C onversely, C++ has no way to declare a class-wide type that is not a
pointer or reference type. Ada has C'Class for just this purpose. The
consequence is that, in Ada, you do not need any pointers to achieve
dynamic dispatching whereas C++ requires you to use pointers if you
want dynamic dispatching. Consider again:

void p () {
object.foo (); // static dispatch
pointer->foo (); // dynamic dispatch
}

There is no way to dispatch dynamically on "object"; you must use
"pointer"; contrast with Ada:

procedure P (Object : C) is
begin
Foo (Object); -- static dispatch
Foo (C'Class (Object)); -- safe dynamic dispatch, without pointers!
end P;

The construct C'Class (Object) is called a "view conversion" in Ada;
it entails no run-time cost and no additional object code in this case
(convert "up" the type hierarchy) but it allows the programmer to
choose whether each call should dispatch statically or dynamically.

HTH

--
Ludovic Brenta.
From: Niklas Holsti on
Dmitry A. Kazakov wrote:
> On Fri, 20 Nov 2009 15:56:50 +0200, Niklas Holsti wrote:
>
>> Dmitry has philosophical objections to redispatching, which is why he
>> suggests that you should declare P with a class-type parameter THIS in
>> the first place, so that A (THIS) will dispatch.
>
> I would not call them philosophical.

Please, Dmitry, suggest a better word; perhaps "methodological"? I don't
want to disparage your view, I understand (I think) the ideas behind it,
although I don't value them in the same way you do.

> If you see the code like
>
> if X = 1 then
> ...
> if X = 1 then
> ...
> end if;
> ...
> if X = 1 then
> ...
> end if;
> ...
>
> you start suggesting that there could be something wrong here.

In the code above, yes, because

if X = 1 then
if X = 1 then
Foo;
end if;
end if;

is statically equivalent to the shorter

if X = 1 then
Foo;
end if;

(assuming that X is not volatile, of course).

But the effects of A (THIS) and A (FOO'Class (THIS)) are different, and
the declaration of an operation as primitive, or class-wide, also has
significant effects on the design.

A better analogy is code like this:

procedure John (X : Integer ..) is
begin
...
if X /= 1 then ...
end if;
...
end Jonh;

procedure Mary (...) is
begin
if X = 1 then
John (X);
else ...
end if;
end Mary;

Yes, there is a double check of X, but no, it is not a redundant check.

> Re-dispatch
> is not an error it is an indication of a potential design problem. Why
> would you need to check twice the same tag? That requires an explanation in
> the design document and comments in the code.

I don't want to repeat the (long) discussion we had on this subject, but
two comments:

- It may be that this is the first check of the tag. The operation that
contains a (re-)dispatching call may have been called with static
binding, not dispatching.

- The "explanation" required in the documents and code is simple, and is
just that redispatching is used to get the most appropriate, specialized
behaviour of this operation.

> A technical issue is that if we had tagged types of value semantics (i.e.
> classes rooted in Boolean, Integer etc), then re-dispatch could not work
> with them.

Agreed, for pure value semantics. But "object identity" is a central
idea in object-oriented programming, so pure value semantics is doubtful
in this area.

> I.e. from Ada's agnostic point of view on by-value vs.
> by-reference, re-dispatch (or speaking more generally, view
> conversion to T'Class) is an implementation-specific kludge.

I think the parameter-passing method (by value or by reference) is not
the central question; the question is whether we have identifiable
objects or not. Objects could be passed by value, that is, the bits
representing the (view of the) object could be copied, but the "value"
would then have to include some kind of identity label (practically
speaking, a reference), which would allow redispatching.

--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
From: Dmitry A. Kazakov on
On Fri, 20 Nov 2009 17:00:44 +0200, Niklas Holsti wrote:

> A better analogy is code like this:
>
> procedure John (X : Integer ..) is
> begin
> ...
> if X /= 1 then ...
> end if;
> ...
> end Jonh;
>
> procedure Mary (...) is
> begin
> if X = 1 then
> John (X);
> else ...
> end if;
> end Mary;

This is a wrong analogy. In your example John serves all values of X. So
when you call it with the value 1 it is not like dispatch, which chooses a
body for just one tag. In other words your example is an analogy to calling
a class-wide John from primitive Mary.

> Yes, there is a double check of X, but no, it is not a redundant check.

But with dispatch the check is redundant.

>> A technical issue is that if we had tagged types of value semantics (i.e.
>> classes rooted in Boolean, Integer etc), then re-dispatch could not work
>> with them.
>
> Agreed, for pure value semantics. But "object identity" is a central
> idea in object-oriented programming, so pure value semantics is doubtful
> in this area.

Why is it doubtful? What is wrong with a class rooted in Boolean?

Anyway object identity, if any, is a domain space animal. The way by which
identity is modeled in the program is the programmer's business. You might
believe in the Platonic Universe where each number is unique with an RFID
chip on it, but that does not oblige you to use reference semantics for
integers.

> > I.e. from Ada's agnostic point of view on by-value vs.
> > by-reference, re-dispatch (or speaking more generally, view
> > conversion to T'Class) is an implementation-specific kludge.
>
> I think the parameter-passing method (by value or by reference) is not
> the central question; the question is whether we have identifiable
> objects or not. Objects could be passed by value, that is, the bits
> representing the (view of the) object could be copied, but the "value"
> would then have to include some kind of identity label (practically
> speaking, a reference), which would allow redispatching.

You are conflating "values" of real world objects (modeled) and values of
the language objects (models). There is a mapping between them, but they
are different. Now, what you call identity is just a value (ID), which is
combined with another value (state). There is no mystery in it. You can
copy ID (like you can do the type tag). You can also copy the state (we
ignore here objects bound to hardware ports, random number generators etc).
This is what would happen if you passed a class-wide object by value. No
problem, and no need to re-dispatch, because no dispatch yet occurred. When
you pass only the state, you have to dispatch. Again, no mystery, you do
not lose the identity, it is now the calee's one. This identity in the
callee is statically known, though you might not know which callee among
many. Re-dispatching from a *given* callee is either redundant or worse, a
clash between the callee's identity and one of the actual object in the
caller. Some call it "type error"...

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Niklas Holsti on
Dmitry A. Kazakov wrote:
> On Fri, 20 Nov 2009 17:00:44 +0200, Niklas Holsti wrote:
>
>> A better analogy is code like this:
>>
>> procedure John (X : Integer ..) is
>> begin
>> ...
>> if X /= 1 then ...
>> end if;
>> ...
>> end Jonh;
>>
>> procedure Mary (...) is
>> begin
>> if X = 1 then
>> John (X);
>> else ...
>> end if;
>> end Mary;
>
> This is a wrong analogy. In your example John serves all values of X.

You are not seeing the analogy that I intended. John does different
things for certain values of X, similar to a primitive operation that is
overridden for certain types (values of X when X is a tag).

> So when you call it with the value 1 it is not like dispatch, which
> chooses a body for just one tag.

The test "if X /= 1" in John is analogous to one dispatch, and the test
"if X = 1" in Mary to another (perhaps it would have been clearer if the
latter had been "if X > 2", or some other pair of less correlated
checks). My point is that it is quite normal and valid to make several
tests of the same value, as long as the tests are not redundant.

>> Yes, there is a double check of X, but no, it is not a redundant check.
>
> But with dispatch the check is redundant.

Obviously it is not, because the program behaves quite differently with
redispatching than without it.

I grant you that for every program that uses redispatching there is an
equivalent program that does not redispatch and computes the same thing.
But I, personally, seem to find the redispatching program simpler to
construct and understand. I also think it is usually shorter, with less
almost-duplicated code.

I won't comment any more on the "object identity" question -- it becomes
too philosophical for me (your reference to Plato motivates the use of
this word :-). Redispatching is a well defined feature of current Ada
that I find useful and not dangerous -- so far, at least.

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