How do I handle errors in methods chains in Perl?

寵の児 提交于 2019-12-02 23:57:12

I handle this by returning a null object at the point of failure. That object responds to every method by simply returning itself, so it keeps doing that until it eats up the remaining methods. At the end, you look in $x to see if it's the result you expected or this null object.

Here's an example of such a thing:

use v5.12;

package Null {
    my $null = bless {}, __PACKAGE__;
    sub AUTOLOAD { $null }
    }

For every method called, the AUTOLOAD intercepts it and returns the empty object.

When you run into an error, you return one of these Null objects. In the middle of a method chain you still get an object back so Perl doesn't blow up when you call the next method.

sub get_other_obj {
    ...;
    return Null->new if $error;
    ...;
    }

At the end of the chain, you can check what you got back to see if it's a Null object. If that's what you got, something bad happened.

That's the basic idea. You can improve on the Null class to make it remember a message and where it was created, or add some polymorphic methods (such as sub is_success { 0 }) to make it play nicely with the interfaces of the objects you expected to get.

I thought I had written something long about this somewhere, but now I can't find it.

You can write a scalar method that will wrap a method chain in error handling:

my $try = sub {
    @_ > 1 or return bless {ok => $_[0]} => 'Try';

    my ($self, $method) = splice @_, 0, 2;
    my $ret;
    eval {
        $ret = $self->$method(@_);
    1} or return bless {error => $@} => 'Try';
    bless {ok => $ret} => 'Try'
};

{package Try;
    use overload fallback => 1, '""' => sub {$_[0]{ok}};
    sub AUTOLOAD {
        my ($method) = our $AUTOLOAD =~ /([^:]+)$/;
        $_[0]{ok} ? $_[0]{ok}->$try($method, @_[1..$#_]) : $_[0]
    }
    sub DESTROY {}
    sub error {$_[0]{error}}
}

to use it:

{package Obj;
    sub new {bless [0]}
    sub set {$_[0][0] = $_[1]; $_[0]}
    sub add {$_[0][0] += ($_[1] || 1); $_[0]}
    sub show {print "Obj: $_[0][0]\n"}
    sub dies  {die "an error occured"}
}

my $obj = Obj->new;

say "ok 1" if $obj->$try(set => 5)->add->add->show; # prints "Obj 7"
                                                    # and "ok 1"

say "ok 2" if $obj->$try('dies')->add->add->show;   # prints nothing 

say $obj->$try('dies')->add->add->show->error;  # prints "an error occured..."

The first line of the $try method also allows the following syntax:

say "ok 3" if $obj->$try->set(5)->add->add->show;

One idea would be to create a class that uses overload to return a false value when an instance object is evaluated in string/number/boolean contexts, but would still allow methods to be called on it. An AUTOLOAD method could always return $self allowing a method chain to propagate the same error.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!