Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Re^3: chaining method calls (perl/DWIM)

by tye (Cardinal)
on Jun 12, 2003 at 23:15 UTC ( #265535=note: print w/ replies, xml ) Need Help??


in reply to Re^2: chaining method calls
in thread chaining method calls

You don't even think timely, well-ordered destructors are important. So what do you know? ;)

The idea is very DWIM and so I think it fits well with Perl. If Perl had better standardization/tools for exception handling, then I'd likely use that much more instead. As is, exception handling in Perl is what I consider to be an "uncommon practice" and likely to surprise people so I usually avoid it (and I regretted one case I can think of where I didn't and in the next release of the module I removed exception throwing).

Because Perl has timely destructors, I don't mind using them. I find them very powerful. There are things that can be done very cleanly in timely destructors that are just plain hard to do without (lock this and without having to remember to put in any code elsewhere, I know the lock will be released when I leave this block; allocate this exclusively and free when we are done with it).

The idea of not having well-ordered destructors just boggles my mind. But that is for another thread.

I also find the distinction between exception and failure (so return a false value) to be a pretty slippery one. One of my favorite things about this idea is that I don't have to decide. I enable you, the module user, to decide which one you want to use and to change your mind depending on the situation.

I also like that it detects coding mistakes where you forget to check whether something failed or not. I really think all Perl's built-in functions should be capable of behaving this way (like use Fatal qw( :void ... ) almost does) at least optionally.

                - tye


Comment on Re^3: chaining method calls (perl/DWIM)
Re: Re^3: chaining method calls (perl/DWIM)
by shotgunefx (Parson) on Jun 13, 2003 at 00:07 UTC
    "The idea of not having well-ordered destructors just boggles my mind. But that is for another thread."

    Mine too. Back to the issue at hand, I don't normally use the FOO->bar->base syntax. I think it would be best in well defined interfaces though with objects/methods that have a psuedo random relationship, it seems like it would be an ugly scene.

    -Lee

    "To be civilized is to deny one's nature."
Re^4: chaining method calls (perl/DWIM)
by adrianh (Chancellor) on Jun 14, 2003 at 22:56 UTC
    You don't even think timely, well-ordered destructors are important. So what do you know? ;)

    Meow :-)

    The idea is very DWIM and so I think it fits well with Perl.

    It is an cute hack and I like the concept. However, unless I am misunderstanding your proposal there are areas where it can cause non-obvious behaviour - unless you're careful.

    For example, assuming method_that_fails returns an error object like the one you outlined:

    { # Would I expect a scope change to radically alter the time an # error is reported? # $x dies at the end of the block # $y dies at some indeterminate time in the future. my $x = Foo->new->method_that_fails->foo; our $y = Foo->new->method_that_fails->foo; }; # This will fail silently because $bar is an error object, $bar->next # will just return $self, which is then evaluated in a boolean context # so the DESTROY won't give an error. Would I remember to catch the # error explicitly in situations like this? What if that while loop # was off in another module? my $bar = $foo->method_that_fails while ($bar->next) { print $bar->current, "\n" }; # Would I expect an error about Foo creation when Bar objects # are destroyed? sub Bar::new { bless { foo => Foo->new->method_that_fails }, shift };

    I know that we had this conversation before but the scoping issue didn't occur to me then. It seems fairly easy to come up with scenarios where the error object doesn't get DESTROYed for quite some time.

    I can see this, in combination with AUTOLOADed methods returning the error object, resulting in errors being ignored or reported a long way from where they occurred.

    As is, exception handling in Perl is what I consider to be an "uncommon practice" and likely to surprise people so I usually avoid it

    Personally I find that exceptions help more than they hinder. I like keeping the error handling from obscuring the mainline. I've made my arguments for exceptions elsewhere so I won't repeat myself here ;-) YMMV.

    Because Perl has timely destructors, I don't mind using them. I find them very powerful. There are things that can be done very cleanly in timely destructors that are just plain hard to do without

    Agree completely. I use them myself on occasion.

    The idea of not having well-ordered destructors just boggles my mind. But that is for another thread.

    The idea of not having some sort of scoped action/finalization mechanism in a language boggles my mind. I just don't think it needs to be duct taped to an otherwise, for me, bleeding annoying memory management system :-)

    I also find the distinction between exception and failure (so return a false value) to be a pretty slippery one.

    It is an interesting distinction. It's not so much between exception and failure as between expected and erroneous behaviour. Is the fact that open returns false an error ("You asked me to open this and I can't. Ack!") or an informative return value ("You asked me to open this if I could and I couldn't. Just thought I'd let you know."). As you correctly point out it's context dependent.

    One of my favorite things about this idea is that I don't have to decide. I enable you, the module user, to decide which one you want to use and to change your mind depending on the situation.

    It is a nice idea but I think there are enough (pardon the pun) exceptions to the DWIMish behaviour to make the error object you suggested more trouble than it's worth.

    I really think all Perl's built-in functions should be capable of behaving this way (like use Fatal qw( :void ... ) almost does) at least optionally.

    Amen. I was so happy when I first discovered Fatal. Then sad to find all the cases where it doesn't work. Hey ho.

      How often have you written code like

      $ref = { foo => open( BAR, "> baz" ), bar => print( BAR "biff\n" ), };
      the above? If you do, then, yes, you write bizarre enough code that my DWIM design will likely not do what you mean.

      This part of the design is only for methods that don't return interesting values, merely success/failure notification (though in the form of an object to enable chaining of methods). So if you are in the habit of squirreling away uninteresting values in long-lived locations that you never check, then, yes, you won't know about failures until global destruction. But then, if you do that and don't have my error object, then you won't know about failures period, so I fail to see the problem. Yes, if I always threw exceptions you could argue that one would know about failures no matter what, but I think you'd find many people would just get very sloppy about catching and ignoring way too many exceptions and you really wouldn't be in that much of a better position.

      I can see some value in "all failures are exceptions", but I really do think it goes too far. Sometimes the failure cases are the reason for the flow and so should be handled as part of the flow, not forced into some exception catcher. Why just this week I was forced to catch some exceptions that really should have been normal failures and it was much more painful that way.

                      - tye

        I don't think code like:

        package Foo::Live; sub new { my $class = shift; return bless { iterator => Iterator->new(class => $class)->where(live => 1); }, class; }; sub next { my $self = shift; $self->{iterator}->next; };

        is particularly bizarre (if you accept the chained method idiom). Yet I wouldn't expect:

        my $foo = Foo::Live->new; while (my $subfoo = $foo->next) { print "$subfoo\n" };

        to fail silently if where returned an error object. These are the sort of, I think, legitimate uses that could cause unexpected results. YMMV

        This part of the design is only for methods that don't return interesting values, merely success/failure notification (though in the form of an object to enable chaining of methods). So if you are in the habit of squirreling away uninteresting values in long-lived locations that you never check, then, yes, you won't know about failures until global destruction.

        When I use the method-chaining idiom I am often interested in the last value. Perhaps this is an argument against method chaining ;-)

        But then, if you do that and don't have my error object, then you won't know about failures period, so I fail to see the problem.

        Depends. In the cases where you are using method chaining you get an error as soon as you try and treat it as an object.

        Yes, if I always threw exceptions you could argue that one would know about failures no matter what,

        You knew what I was going to say didn't you :-)

        but I think you'd find many people would just get very sloppy about catching and ignoring way too many exceptions and you really wouldn't be in that much of a better position.

        I guess part of it is that I like to have a uniform error handling mechanism across a project. My mental facilities are too meagre to spend time considering whether a normal return value, an error object or an exception is more appropriate. So I just throw an exception. In my personal experience this helps more than it hinders.

        I can see some value in "all failures are exceptions", but I really do think it goes too far. Sometimes the failure cases are the reason for the flow and so should be handled as part of the flow, not forced into some exception catcher. Why just this week I was forced to catch some exceptions that really should have been normal failures and it was much more painful that way.

        I just never find it that much hassle. I still code so that success is indiciated by a true return value. That way you can always write:

        $o->foo unless eval { $foo->bar };

        avoiding nasties like

        eval { $foo->bar }; $o->foo if $@;

        When the exception is the driver for the mainline.

        Anyway - I think this is probably a to-may-to/to-mah-to issue so I'll shutup and stop being annoying :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://265535]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2014-08-23 03:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (172 votes), past polls