* issue with implementing a visitor design pattern @ 2004-01-15 19:22 cl1 2004-01-17 3:41 ` Robert I. Eachus 0 siblings, 1 reply; 10+ messages in thread From: cl1 @ 2004-01-15 19:22 UTC (permalink / raw) i'm trying to implement a visitor pattern for a parse tree(its a simple calculator at this point). i have an abstract type: type Visitor_Record is abstract tagged null record; with primative operations Visit() for each node type like so: Visit(V : in out Visitor_Record; Node : in Add_Op'Class) is abstract; ... Visit(V : in out Visitor_Record; Node : in Assignment_Node'Class) is abstract; what i'm trying to do is implement a Preorder_Visitor_Record type that uses the Visit() operation to traverse the tree and calls an operation Visit_Operation() that is also a primative operation on Preorder_Visitor_Record like so: with parse_tree_pkg; use parse_tree_pkg; -- has the parse tree nodes a primative operation Accept_Visitor() for each node -- and the abstract Visitor_Record with primative operations Visit() for each node package Preorder_Visitor_Pkg is type Preorder_Visitor_Record is new Visitor_Record with null record; -- this is overidiing the abstract version in parse_tree_pkg; procedure Visit ( V : in out Preorder_Visitor_Record; Node : in Add_Op'Class); ..... procedure Visit_Operation (V : in preorder_Visitor_Record; node : in add_op'class); end preorder_visitor_Pkg; package body Preorder_Visitor_Pkg is procedure Visit_Operation ( V : in out Preorder_Visitor_Record; Node : in Add_Op'Class) is begin raise Not_Implemented; -- better handling in actual code with an error message using ada.exceptions end Visit_Operation procedure Visit( V : in out Preorder_Visitor_Record; Node : in Add_Op'Class) is begin Visit_Operation(V, Node); end Visit; the actual code errors saying ambiguos call of Visit_Operation() from within the Visit() procedure. with possible interpretations at the definition of Preorder_Visitor_Record and at the specification for Visit_Operation. the code works when you take Visit_Operation out of the mix. the package compiles and works with a test driver. but when i try to add in this Visit_Operation it doesn't work. I don't know if there are some scoping issues or if there is something else i need to know about primative operations to make this work. any guidance would be appreciated. Thanks, Charles Lambert ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-15 19:22 issue with implementing a visitor design pattern cl1 @ 2004-01-17 3:41 ` Robert I. Eachus 2004-01-17 21:24 ` Robert A Duff 2004-01-17 23:05 ` cl1 0 siblings, 2 replies; 10+ messages in thread From: Robert I. Eachus @ 2004-01-17 3:41 UTC (permalink / raw) cl1 wrote: > i'm trying to implement a visitor pattern for a parse tree(its a simple > calculator at this point). i have an abstract type: I'll try to help... > > type Visitor_Record is abstract tagged null record; > > with primative operations Visit() for each node type like so: > > Visit(V : in out Visitor_Record; Node : in Add_Op'Class) is abstract; > ... > Visit(V : in out Visitor_Record; Node : in Assignment_Node'Class) is > abstract; Do not make Visit classwide. In other words get rid of the 'Class in the definition. For dynamic dispatching where you may need 'Class is as the type of the formal parameter in the call. > what i'm trying to do is implement a Preorder_Visitor_Record type that uses > the Visit() operation to > traverse the tree and calls an operation Visit_Operation() that is also a > primative operation on Preorder_Visitor_Record > like so: > with parse_tree_pkg; use parse_tree_pkg; -- has the parse tree nodes a > primative operation Accept_Visitor() for each node > -- and the > abstract Visitor_Record with primative operations Visit() for each node > package Preorder_Visitor_Pkg is > type Preorder_Visitor_Record is new Visitor_Record with null record; > > -- this is overidiing the abstract version in parse_tree_pkg; But it doesn't in Ada and that is your problem. It overloads not overrides in some scopes. Ada 200Y will probably have an overrides keyword which will get you an error message at compile time here. > procedure Visit ( V : in out Preorder_Visitor_Record; Node : in > Add_Op'Class); > ..... > > procedure Visit_Operation (V : in preorder_Visitor_Record; node : in > add_op'class); > end preorder_visitor_Pkg; Same problem here... > > package body Preorder_Visitor_Pkg is > > procedure Visit_Operation ( V : in out Preorder_Visitor_Record; Node : in > Add_Op'Class) > is begin > raise Not_Implemented; -- better handling in actual code with an error > message using ada.exceptions > end Visit_Operation > procedure Visit( V : in out Preorder_Visitor_Record; Node : in Add_Op'Class) > is begin > Visit_Operation(V, Node); > end Visit; > > > the actual code errors saying ambiguos call of Visit_Operation() from within > the Visit() procedure. with possible interpretations at the definition of > Preorder_Visitor_Record and at the specification for Visit_Operation. > > the code works when you take Visit_Operation out of the mix. the package > compiles and works with a test driver. but when i try to add in this > Visit_Operation it doesn't work. I don't know if there are some scoping > issues or if there is something else i need to know about primative > operations to make this work. any guidance would be appreciated. > > Thanks, > Charles Lambert The much deeper problem you have is that the Visitor pattern is a waste of effort in Ada. Not wrong, or unimplementable, but the pattern as such is much heavier weight than it needs to be. The normal approach to this problem in Ada is, in the package that declares the abstract data type, (the parse tree in this case) to have a generic procedure to do the work: package Parse_Tree_Pkg is -- I'd call it Parse_Trees, but that is a -- style issue... type Node is ...; type Parse_Tree is ...; generic with procedure Visitor (N: in out Node); procedure In_Order (PT: in out Parse_Tree); -- visit all the nodes -- of a parse tree in -- order and Visitor -- Visitor; Now you can instantiate In_Order with whatever visitor you want. Much easier to work with, and easier to change the In_Order instantiation to Pre_Order or Post_Order if the design changes. Note that the generic formal procedure doesn't mention the ADT in its profile. So you can pass the same concrete visitor implementation just as easily to a queue or stack, as long as the node type is the same. -- Robert I. Eachus "The war on terror is a different kind of war, waged capture by capture, cell by cell, and victory by victory. Our security is assured by our perseverance and by our sure belief in the success of liberty." -- George W. Bush ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-17 3:41 ` Robert I. Eachus @ 2004-01-17 21:24 ` Robert A Duff 2004-01-24 20:38 ` Dale Stanbrough 2004-01-17 23:05 ` cl1 1 sibling, 1 reply; 10+ messages in thread From: Robert A Duff @ 2004-01-17 21:24 UTC (permalink / raw) "Robert I. Eachus" <rieachus@comcast.net> writes: > The much deeper problem you have is that the Visitor pattern is a waste > of effort in Ada. Not wrong, or unimplementable, but the pattern as > such is much heavier weight than it needs to be. I agree. In fact, I'd say "visitor" is my least favorite design pattern of the ones in the Gamma et al book. "Visitor" has all the disadvantages of a case statement (over dispatching calls), with none of the advantages! - Bob ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-17 21:24 ` Robert A Duff @ 2004-01-24 20:38 ` Dale Stanbrough 2004-01-24 21:44 ` Robert A Duff 0 siblings, 1 reply; 10+ messages in thread From: Dale Stanbrough @ 2004-01-24 20:38 UTC (permalink / raw) Robert A Duff wrote: > "Robert I. Eachus" <rieachus@comcast.net> writes: > > > The much deeper problem you have is that the Visitor pattern is a waste > > of effort in Ada. Not wrong, or unimplementable, but the pattern as > > such is much heavier weight than it needs to be. > > I agree. In fact, I'd say "visitor" is my least favorite design pattern > of the ones in the Gamma et al book. "Visitor" has all the > disadvantages of a case statement (over dispatching calls), > with none of the advantages! If you have two orthoganal classifications (object and function) and you need to (effectively) double dispatch, the visitor pattern is quite useful. Not quite sure how else you would achieve it in Ada. Dale -- dstanbro@spam.o.matic.bigpond.net.au ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-24 20:38 ` Dale Stanbrough @ 2004-01-24 21:44 ` Robert A Duff 2004-01-26 8:34 ` Dmitry A. Kazakov 0 siblings, 1 reply; 10+ messages in thread From: Robert A Duff @ 2004-01-24 21:44 UTC (permalink / raw) Dale Stanbrough <MrNoSpam@bigpoop.net.au> writes: > Robert A Duff wrote: > > I agree. In fact, I'd say "visitor" is my least favorite design pattern > > of the ones in the Gamma et al book. "Visitor" has all the > > disadvantages of a case statement (over dispatching calls), > > with none of the advantages! > > If you have two orthoganal classifications (object and function) and > you need to (effectively) double dispatch, the visitor pattern is > quite useful. Not quite sure how else you would achieve it in Ada. You can have a variant record, with a discriminant of an enumeration type, and use case statements on the discriminant. You can even use subtypes of the enumeration in the case statements. The way things were done in the olden days. ;-) You have to decide which of "adding new types" or "adding new operations" is the more important issue. If the former, the normal OOP style is appropriate. If the latter, case statements are appropriate. My objection to the "visitor" pattern is that it simulates case statements, but with a whole lot of verbose mechanistic code. And it retains all the disadvantages of the case-statement style. Now, OO advocates will tell us that case statements are evil -- one ought to define dispatching operations, so adding a new type (or "class") will not require editing all those case statements. Well, that's quite true in the normal case where adding new types is the main issue. But when you use the "visitor" pattern, you've got essentially the same issue of editing all the operations to handle the new case. I suspect much of OO advocates' hatred of case statements comes from languages where case/switch statements automatically default to something-or-other. But Ada largely solves that problem via its full-coverage rule (assuming you don't evilly use 'when others'). You still have to edit all the case statements, which is particularly bad if we're talking about a widely-used library, but at least in Ada, the compiler tells you which case statements have missing cases. If you want to mix the two styles, you're kind of stuck (in Ada, and in pretty much every other OOP language). You have to use the visitor pattern. If Ada allowed case statements on the 'Tag field of class-wide objects, then things would be much easier. OO advocates don't like explicit tests on the 'Tag, and that's usually right, but when it's necessary, I claim a simple case statement would be preferable to the "visitor" pattern, which does essentially the same thing, but with a whole lot of extra baggage. Adding "case on 'Tag" to Ada would not be trivial. One would have to reconcile the open-ended type extension capability of tagged types with the full-coverage rules of case statements. - Bob ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-24 21:44 ` Robert A Duff @ 2004-01-26 8:34 ` Dmitry A. Kazakov 2004-01-26 20:18 ` Robert A Duff 0 siblings, 1 reply; 10+ messages in thread From: Dmitry A. Kazakov @ 2004-01-26 8:34 UTC (permalink / raw) On 24 Jan 2004 16:44:02 -0500, Robert A Duff <bobduff@shell01.TheWorld.com> wrote: >Dale Stanbrough <MrNoSpam@bigpoop.net.au> writes: > >> Robert A Duff wrote: >Adding "case on 'Tag" to Ada would not be trivial. One would have to >reconcile the open-ended type extension capability of tagged types with >the full-coverage rules of case statements. Same as with integer types, an obligatory: when others => ... Then, of course, there could be subtypes of classes and thus finite sets of tags: type T is tagged ...; type T1 is new T with ...; type T2 is new T1 with ...; ... type TN is new ...; subtype Finite is T'Class (T1..TN); Also useful would be dispatching on bare tags or an equivalent of that. -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-26 8:34 ` Dmitry A. Kazakov @ 2004-01-26 20:18 ` Robert A Duff 2004-01-27 8:36 ` Dmitry A. Kazakov 0 siblings, 1 reply; 10+ messages in thread From: Robert A Duff @ 2004-01-26 20:18 UTC (permalink / raw) Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> writes: > On 24 Jan 2004 16:44:02 -0500, Robert A Duff > <bobduff@shell01.TheWorld.com> wrote: > > >Dale Stanbrough <MrNoSpam@bigpoop.net.au> writes: > > > >> Robert A Duff wrote: > > >Adding "case on 'Tag" to Ada would not be trivial. One would have to > >reconcile the open-ended type extension capability of tagged types with > >the full-coverage rules of case statements. > > Same as with integer types, an obligatory: > > when others => > ... "when others" *defeats* the full-coverage rules. It should be used only if you know it covers *all* the other cases, including the ones that haven't been invented yet! > Then, of course, there could be subtypes of classes and thus finite > sets of tags: Maybe... - Bob ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-26 20:18 ` Robert A Duff @ 2004-01-27 8:36 ` Dmitry A. Kazakov 2004-02-01 0:32 ` Georg Bauhaus 0 siblings, 1 reply; 10+ messages in thread From: Dmitry A. Kazakov @ 2004-01-27 8:36 UTC (permalink / raw) On 26 Jan 2004 15:18:41 -0500, Robert A Duff <bobduff@shell01.TheWorld.com> wrote: >Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> writes: > >> On 24 Jan 2004 16:44:02 -0500, Robert A Duff >> <bobduff@shell01.TheWorld.com> wrote: >> >> >Dale Stanbrough <MrNoSpam@bigpoop.net.au> writes: >> > >> >> Robert A Duff wrote: >> >> >Adding "case on 'Tag" to Ada would not be trivial. One would have to >> >reconcile the open-ended type extension capability of tagged types with >> >the full-coverage rules of case statements. >> >> Same as with integer types, an obligatory: >> >> when others => >> ... > >"when others" *defeats* the full-coverage rules. It should be used only >if you know it covers *all* the other cases, including the ones that >haven't been invented yet! It is a strange thing, "when others". Especially when others (:-)) is empty, like in: subtype Empty is String (0..-1); X : Empty := (others => ' '); -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-27 8:36 ` Dmitry A. Kazakov @ 2004-02-01 0:32 ` Georg Bauhaus 0 siblings, 0 replies; 10+ messages in thread From: Georg Bauhaus @ 2004-02-01 0:32 UTC (permalink / raw) Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote: : : It is a strange thing, "when others". Especially when others (:-)) is : empty, Isn't that the vacuousity that you are asked to hammer into your brain the moment you enter the study of mathematical subjects? :-) ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: issue with implementing a visitor design pattern 2004-01-17 3:41 ` Robert I. Eachus 2004-01-17 21:24 ` Robert A Duff @ 2004-01-17 23:05 ` cl1 1 sibling, 0 replies; 10+ messages in thread From: cl1 @ 2004-01-17 23:05 UTC (permalink / raw) after having read all the info given here in the newsgroup on this. I definately open to implementing it with the generics, but i would like to have it working with the set up i have now. I have written lots of notes on what i have done so far and getting it to work in the format i have now would better help me explain why i changed when i post my writtings to the internet. here is a link to some example source: http://www.geocities.com/cwlambert76/preorder_visitor.adb.txt it contains example code of both packages i have written so far. what i have so far is a parse tree, along with an Accept_Visitor() operation for each node. and an abstract Visitor in my parse_tree_pkg with a Visit() operation for each node. i want to implement the different types of tree traversal only once(preorder, inorder, etc.) that is why i have created the preorder_visitor_pkg. and any visitor that needs to "visit" the parse tree can pick its traversal method by subclassing the appropriate visitor package and overiding the Visit_Operation for each node, and in turn not have to implement the actual traversal itself. Now i know this may not be the best way to implement this, at this point the only way to catch that a Visit_Operation() isn't implemented is at runtime (not good). However this has been a great learning process for me, and i would like to be able to share my learning experiances with everyone. So once i have this working then I will be more than happy to refactor my code to a higher quality version, and i would greatly appreciate all of your help in doing so. Thanks, Charles Lambert ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2004-02-01 0:32 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2004-01-15 19:22 issue with implementing a visitor design pattern cl1 2004-01-17 3:41 ` Robert I. Eachus 2004-01-17 21:24 ` Robert A Duff 2004-01-24 20:38 ` Dale Stanbrough 2004-01-24 21:44 ` Robert A Duff 2004-01-26 8:34 ` Dmitry A. Kazakov 2004-01-26 20:18 ` Robert A Duff 2004-01-27 8:36 ` Dmitry A. Kazakov 2004-02-01 0:32 ` Georg Bauhaus 2004-01-17 23:05 ` cl1
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox