Prev: Operation can be dispatching in only one type
Next: Genode implements zero-footprint runtime for Ada and SPARK
From: Dmitry A. Kazakov on 20 Nov 2009 09:31 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 20 Nov 2009 09:54 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 20 Nov 2009 10:00 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 20 Nov 2009 13:44 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 20 Nov 2009 15:09 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 . @ .
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 Prev: Operation can be dispatching in only one type Next: Genode implements zero-footprint runtime for Ada and SPARK |