Re: Git.pm

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 10 May 2012 22:55, Andrew Sayers <andrew-git@xxxxxxxxxxxxxxx> wrote:
> Try::Tiny is an increasingly standard part of Perl - for example, it's
> used extensively in Moose.  There's a good list of arguments about why
> you should use it instead of eval in the Try::Tiny documentation:
> http://search.cpan.org/~nuffin/Try-Tiny-0.01/lib/Try/Tiny.pm
>
> Now I've got that talking point done, here's what I really think :)
>
> Try::Tiny is designed on the assumption that throwing and catching
> objects is something people should do all the time, and it can cause
> subtle errors that are only worth the hassle if you get a lot of benefit
> from doing so.  It's easy enough to come up with ideas for where they
> might be useful, but in the real world advanced uses for exceptions are
> usually a sign you're doing it wrong.  Three of the most common reasons
> for frequent/complex exceptions are handling errors further up the call
> stack, recovering from operations that fail, and clever error-handling.
>
>
> If you want exceptions to be caught by code further up the call stack
> than the immediate caller, you're likely to be disappointed.  This is
> one of the places where "separation of concerns" applies - if I use a
> module that uses a module that uses your module, then catching
> exceptions from your code will just cause my program to break when some
> module in the middle obscures your error by adding its own layer of
> error handling.
>
>
> If you have an operation that really might fail, and you want to
> encourage most people to handle it most of the time, it's better to have
> a function with a meaningful name and good documentation.  This puts the
> burden on the calling function to handle the error instead of letting
> them think "oh well, if it dies someone else will handle it".  It also
> forces you to split functions along boundaries that make your code
> readable, instead of falling for the temptation to make something that
> "just works"... until it doesn't, and the maintainer has to go
> spelunking through code they don't know.  So instead of:
>
>   try {
>       Foo::frobnicate( widgets => 3 );
>   } catch {
>      if (ref($_) eq 'Error::Widget') {
>          die "Could not add 3 widgets";
>      }
>   }
>
> It's better to ask the people using your module to write:
>
>   my $foo = Foo->new;
>   $foo->add_widgets(3) or die "could not add 3 widgets"
>   $foo->frobnicate;
>
> This is easier to document, easier to write and easier to read.
>
>
> If you have an operation where calling code is supposed to do something
> more complicated than give up, it's better to use a callback.  This
> gives you an opportunity to document what's needed, and to check that
> the calling code is doing the right thing before it's too late.  So
> instead of:
>
>    my $widgets = 3;
>    while ( $widgets ) {
>
>        try {
>            Foo::frobnicate($widgets);
>            $widgets = 0;
>        } catch {
>            if ( $_->{remaining_widgets} < 2 ) {
>                die $_->{error};
>            } elsif ( $_->{remaining_widgets} == 2 ) {
>                $widgets = 0;
>            }
>        }
>
>    }
>
> It's better to ask people using your module to write:
>
>    Foo::Frobnicate(
>        widgets => 3,
>        error_handler => sub {
>             my ( $remaining_widgets, $error ) = @_;
>             die $error if $broken_widgets < 2;
>             return "give up" if $remaining_widgets == 2;
>             return "continue";
>        },
>    );
>
> Again, this is more readable and easier to document.
>
>
> Aside from the philosophical angle, Try::Tiny is particularly hard to
> maintain because it looks like a language extension, but is actually
> just an ordinary module.  The try {} and catch {} blocks are anonymous
> subroutines, which lead to some wonderfully unintuitive behaviour.  See
> what you think these do, then run the code to find out:
>
>
>    sub foo {
>        try {
>            return 1;
>        }
>        return 0;
>    }
>
>    sub bar {
>
>        our @args = @_;
>        our @ret;
>
>        try {
>            @ret =
>                wantarray
>                    ?   grep( /blah/, @args )
>                    : [ grep( /blah/, @args ) ]
>        };
>
>        return @ret;
>    }
>
>    my $foo = "bar";
>    sub baz {
>        my $foo = "baz";
>        try {
>            print $foo;
>        }
>    }
>
>    sub qux {
>        my $ret;
>        try {
>            $ret = "value";
>        }
>        return $ret;
>    }
>
>    print foo, "\n";
>    print bar( "blah", "blip" ), "\n";
>    baz;
>    print qux, "\n";
>
>
> In short, Try::Tiny looks like a lot of gain for not much pain, but
> actually it's the other way around.

Total agreement.

Yves
-- 
perl -Mre=debug -e "/just|another|perl|hacker/"
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]