Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Re: RFC: Class::Proxy::MethodChain

by Aristotle (Chancellor)
on Feb 21, 2003 at 15:54 UTC ( #237466=note: print w/replies, xml ) Need Help??

in reply to RFC: Class::Proxy::MethodChain

I've thought about this some more, and written a few more examples.

This example shows the translation of using aggregate object accessors to the appropriate C:P:MC syntax:


my $file_dialog = Gtk::FileSelection->new("File Selection Demo"); $file_dialog->ok_button->label("Load"); $file_dialog->ok_button->relief("half"); $file_dialog->ok_button->width(80); $file_dialog->ok_button->height(50); $file_dialog->ok_button->signal_connect(clicked => sub { print $file_dialog->get_filename(), "\n"; }); $file_dialog->cancel_button->label("Exit"); $file_dialog->cancel_button->relief("half"); $file_dialog->cancel_button->width(80); $file_dialog->cancel_button->height(50); $file_dialog->cancel_button->signal_connect(clicked => sub{Gtk->main_q +uit}); $file_dialog->set_filename("penguin.png"); $file_dialog->signal_connect(destroy => sub{Gtk->main_quit}); $file_dialog->show();
my $file_dialog; $file_dialog = Gtk::FileSelection ->Class::Proxy::MethodChain::new("File Selection Demo") ->ok_button->ret_wrap(sub {shift ->label("Load") ->relief("half") ->width(80) ->height(50) ->signal_connect(clicked => sub { print $file_dialog->get_filename()->ret_val, "\n"; }); }) ->cancel_button->ret_wrap(sub {shift ->label("Exit") ->relief("half") ->width(80) ->height(50) ->signal_connect(clicked => sub{Gtk->main_quit}); }) ->set_filename("penguin.png") ->signal_connect(destroy => sub{Gtk->main_quit}) ->show();

I think it's easy to see that the latter syntax scales much better.

I had been thinking that the ->ret_val() syntax is just too bulky and was toying with the idea of having AUTOLOAD() extract pre(or suf)fixes from the called method's name. Eventually I decided against it - I don't think it's better as it'll only cover one case (albeit probably the most or second most common one - getting the return value of the last method call) and complicate AUTOLOAD() needlessly. A name change on C:P:MC's methods would do just as good a job without the added hassle.

I'm thinking of ->_ret() instead of ->ret_val() and ->_proxy() instead of ->ret_wrap(). ->ret_val() is horribly named to begin with, in retrospect - a function can return more than a single value, after all. I am leaning on ->_proxy() because ->_wrap() strikes me as indescriptive.

Now I know underscores are usually used for private methods and that's actually exactly the reason I'd like to use them here. It's unlikely that another module will expose public methods with such names, which minimizes the potential need to reach for the ->call() escape. For the same reasons I'm pondering renaming it to ->_call().

Of course all that means the ->wrap() constructor becomes ->proxy() as well for consistency and leads me to wondering if it's not maybe a good idea to make that one an exported function instead of a constructor. That way no C:P:MC method would start without an underscore, so collisions with wrapped object's public methods' names are far less likely.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re: Re: RFC: Class::Proxy::MethodChain
by sauoq (Abbot) on Feb 21, 2003 at 20:30 UTC
    I think it's easy to see that the latter syntax scales much better.

    I don't see that. Even with your short example and, I think, a good understanding of your module, I find the "before" much easier to read than the "after." I'm having a hard time even determining whether the "after" version is correct or not. For instance, you are creating a closure within a method call within an assignment to the very variable your closure uses... does that work? What is the value of $file_dialog there?

    The fact that you can't prevent collisions between your methods and those of the class you are proxying is a real problem. The best you can do is make the collisions less likely by requiring the user to deal with uglier method names. Breaking tradition and using underscores for public methods doesn't seem satisfactory. (Wouldn't you rather reinforce a tradition which makes good sense?)

    It seems that all your module is really doing is providing some syntactic sugar to avoid some typing. You can make the "before" code cleaner without resorting to OO contortions. One way to clean it up a bit:

    my $file_dialog = Gtk::FileSelection->new("File Selection Demo"); { my $b = $file_dialog->ok_button; $b->label("Load"); $b->relief("half"); $b->width(80); $b->height(50); $b->signal_connect(clicked => sub { print $file_dialog->get_filename(), "\n"; }); } { my $b = $file_dialog->cancel_button; $b->label("Exit"); $b->relief("half"); $b->width(80); $b->height(50); $b->signal_connect(clicked => sub { Gtk->main_quit }); } $file_dialog->set_filename("penguin.png"); $file_dialog->signal_connect(destroy => sub { Gtk->main_quit }); $file_dialog->show();
    And you could do quite a bit better if you wrote a little utility function to handle the common button initialization.

    In summary, I don't see a real benefit to using this module. I would avoid it as it doesn't seem to do much but provide a small syntactical shortcut, and that, IHMO, at the cost of clarity. For what it's worth, I can't think of a better way to do what you are attempting to do. (I just wouldn't attempt it at all.)

    "My two cents aren't worth a dime.";
      For instance, you are creating a closure within a method call within an assignment to the very variable your closure uses... does that work?
      Look carefully. The my declaration stands by itself at the top, in a separate statement.
      What is the value of $file_dialog there?
      That closure is a callback; nothing is called on $file_dialog at the time of its definition, just as one should expect from any other Perl closure. The value of $file_dialog will be a reference to the C::P::MC object wrapping a fileselect box widget, at the time the closure actually gets called.
      all your module is really doing is providing some syntactic sugar to avoid some typing.
      Correct, except for the "some" bit.
      One way to clean it up a bit:

      Frankly, yuck. I'd rather use the style from my before example than break all rules of indentation - at least with the before example's style, I can use Vim's "visual block" editing features (select a rectangular area, edit one line, same changes get applied to all the line fragments within the selected block) to make my life easier. And it doesn't break my autoindent.

      Also, consider that very simple Gtk Perl app I've written. It's just a main window, a few buttons, a scrollable columnated list, a statusbar and a fileopen dialog - in other words, about the most minimal amount of GUI to do something useful. The code weighs in at well beyond two dozen widgets to set up - imagine how many variables that translates to. Now consider that the list of method calls against each is typically eight to a dozen lines and you get an idea for how much redundancy there is.

      My sense of Once And Only Once nearly caused me seizures.

      What makes it even more revolting is that the majority of these are actually going to be temporary variables, because once I've set up the corresponding widget it does its job on its own and I am no longer interested in it at all. (Scrollbars come to mind.)

      merlyn addressed that with a topicalizing for, but then you have the problem that you must weave calls against different objects, which gets really messy real quick like. And it breaks down as soon as you nest more than one widget deep, such as a Button in a VBox in a Window, because you must weave calls against different objects in the same block, and "it"/$_ can only alias one object at a time.

      There is no good way to get rid of the temporaries without writing a layer of my own syntactic sugar to insert between the main program and the widgets.

      And you could do quite a bit better if you wrote a little utility function to handle the common button initialization.

      (You realize that these are semicontrived examples. There is hardly any redundancy in method name and parameter set patterns in actual code.)

      Did you take a look at multiple method calls against the same object (f.ex GUI programming)? It is indeed just a utility function, albeit admittedly not what you were talking about. The idea behind that snippet evolved into this module. That snippet does indeed work well; I get rid of nearly all temporaries and only need to mention the object of interest once. But using it is not a satisfactory solution in the long run - it introduces its very own (and very clumsy) syntax.

      Consider on the other hand, as merlyn points out in reply to aforementioned node, that properly set up OO modules in Perl will allow chained method calls natively. Returning $self is what one should generally do wherever there's nothing else to do. Examples include Tk and Data::Dumper.

      Gtk doesn't make the list.

      Using C::P::MC, then, allows me to reclaim the benefit of established Perlisms.

      GUI programming is verbose by definition. Setting up 5 properties each on 30 widgets is just going to take a lot of code whichever way you turn it. It helps if at least you don't have to type everything in quadruplicate (or more).

      For all these reasons, I am not going to abandon this idea.

      However, I am not pretending to have arrived at the best possible solution.

      Breaking tradition and using underscores for public methods doesn't seem satisfactory.

      It was more of an idle thought, and I agree with your point. Yes, the fact I can't prevent collisions is a real problem I'll have to get around.

      One idea might be to move all functionality into AUTOLOAD; I could then add another "method name" like to_inner that serves to set an escape flag causing the next call to be passed down to the real object without examining the called method name. That would actually prevent collisions, albeit at the cost of verbose syntax when requesting so.

      The likelyhood of that happening could be reduced by consolidating other functions (ret_wrap can be merged into ret_val and behaviour be selectable via a parameter flag) or using another unlikely prefix such as something that involves a double underscore; maybe p__foo where p is for proxy.

      I'm inclined to do the latter, and actually considering to split functionality to more methods. Something like p__ret to get the return value, p__do for passing a closure, p__wrap for a wrapped aggregate object and p__wrapdo for a closure that takes a wrapped aggregate object seems to make sense.

      Makeshifts last the longest.

        Look carefully.

        That's precisely my point. I'd rather not have to look carefully. Looking carefully should be required by tricky algorithms and obsfucations, not by a series of method calls.

        Correct, except for the "some" bit.

        I don't see that as a high goal. Typing is easy to begin with, and we can avoid most repetition in it by learning to drive our editors more expertly. Besides, it is much more important that code be easy to read than be easy to write. I agree, of course, that what is easy and what isn't mostly boils down to a matter of personal preference.

        I'm not so sure that personal preference alone should govern this case, though. I think this is kludgey at best, and I think I'm being pretty objective about that. You have written a class that does nothing but provide syntactical sugar. It doesn't do so transparently. In some cases it even seems to encourage mixing method calls from two different classes. Also, though unlikely to be a problem in most cases, the code is far from efficient.

        My sense of Once And Only Once nearly caused me seizures.

        I think this was a false application of that principle. How many times you type a variable name is irrelevant. If you find that your code is performing the same tasks repeatedly, in different places, then the principle applies. In that case, you consolidate the duplicated code and limit the number of pathways thereby reducing the chance for error. Your module actually increases the number of potential pathways through the code. For instance, someone might access a method through your proxy module or directly.

        If this is really an attempt to address something that bothers you with Gtk in particular, then maybe you should limit your module to being an auxillary module specifically for use with Gtk. That way, you could get around method name collisions through documentation. The inefficiency issue would probably never matter. And it would encourage the method chaining idiom in the very area you wished to see it. If it really caught on, maybe the Gtk authors would add support for it.

        "My two cents aren't worth a dime.";

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://237466]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (5)
As of 2018-02-18 20:25 GMT
Find Nodes?
    Voting Booth?
    When it is dark outside I am happiest to see ...

    Results (257 votes). Check out past polls.