Rozróżnij wyjątek od awarii w bloku CATCH [RAKU]

9

Wiemy, że awarią może zaradzić blok CATCH.

W poniższym przykładzie tworzymy błąd „AdHoc” (w innym sub-sub) i obsługujemy wyjątek w bloku CATCH (w moim sub-sub)

sub my-sub {
    try {
        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;
    }
}

sub other-sub { fail 'Failure_X' }

my-sub();

Dane wyjściowe są następujące:

AdHoc Exception handled here
This was a Failure

Moje pytanie brzmi jednak: jak odróżnić awarię od „normalnego” wyjątku w bloku CATCH, aby rozróżnić te dwa przypadki?

jakar
źródło

Odpowiedzi:

12

Zależność między Failurei Exceptionpolega na tym, że a Failurema Exception- to znaczy, że przechowuje obiekt wyjątku jako część swojego stanu. Coś takiego:

class Failure {
    has Exception $.exception;
    # ...
}

Kiedy Failure„eksploduje”, robi to, wrzucając to, Exceptionco jest w środku. Tak więc to, co dociera do CATCHbloku, to Exceptionobiekt i nie ma żadnego łącza z powrotem do otaczającego Failure. (W rzeczywistości dany Exceptionprzedmiot może w zasadzie być w posiadaniu wielu osób Failure.)

Dlatego nie ma bezpośredniego sposobu na wykrycie tego. Z punktu widzenia projektowania prawdopodobnie nie powinieneś być i powinieneś znaleźć inny sposób rozwiązania swojego problemu. A Failurejest po prostu sposobem na odłożenie wyjątku i umożliwienie traktowania go jako wartości; nie ma na celu zmiany charakteru problemu leżącego u podstaw problemu, ponieważ jest on przekazywany jako wartość, a nie jako natychmiastowe przeniesienie przepływu sterowania. Niestety pierwotny cel nie został podany w pytaniu; przydatne może okazać się przyjrzenie się wyjątkom kontrolnym, ale w innym przypadku może zadać kolejne pytanie na temat problemu, który próbujesz rozwiązać. Prawdopodobnie jest lepszy sposób.

Dla kompletności, będę pamiętać, że nie pośrednie sposoby, które można wykryć, że Exceptionzostał wyrzucony przez Failure. Na przykład, jeśli uzyskasz .backtraceobiekt wyjątku i spojrzysz na pakiet górnej ramki, możesz ustalić, że pochodzi on z Failure:

sub foo() { fail X::AdHoc.new(message => "foo") }
try {
    foo();
    CATCH {
        note do { no fatal; .backtrace[0].code.package ~~ Failure };
        .resume
    }
}

Jest to jednak w dużej mierze zależne od szczegółów implementacji, które można łatwo zmienić, więc nie polegam na tym.

Jonathan Worthington
źródło
Żeby to wyjaśnić, mam zamiar zajmować się tylko wyjątkami (w bloku CATCH). W przypadku awarii chcę wznowić, jakby nic się nie wydarzyło, i niech resztę kodu (poza CATCH) obsłuży awaria. W moim przykładzie nie spodziewałem się, że zwrócony błąd nie wyzwoli zawartego wyjątku! Wszystko, co zrobiłem, to uzyskanie wyniku w $ b i sprawdzenie go jako Bool. Moim zdaniem nie oznacza to „wykorzystania” Awarii, a zatem uruchomienia bloku CATCH! Zamiast tego wydaje się, że POŁOW zawsze obsługuje wyjątek zawarty w awarii !!
jakar
Ponadto w twoim przykładzie dotyczącym pośredniego sposobu wykrywania awarii zwrócony Bool (z inteligentnego sprawdzania typu awarii) ma wartość „False”. Ale spodziewałem się, że to będzie „prawda”! Przegapiłem coś???
jakar
1
@jakar tryBlok implikuje pragmę use fatal, co oznacza, że ​​każde zwrotne Failurewywołanie wykonane w bloku jest natychmiast przekształcane w wyjątek. Po prostu nie używaj try; a CATCHmoże przejść w dowolnym bloku w Raku (więc po prostu trzymaj go na poziomie sub). Możesz też napisać no fatalu góry trybloku.
Jonathan Worthington
A co z moim drugim komentarzem?
jakar
1
Na przykładzie podałem wydruki Truena wersji Rakudo, którą mam lokalnie. Jeśli to nie zależy od ciebie, to tylko dowodzi, że kruchość jest taka.
Jonathan Worthington
6

Wystarczy usunąć tryopakowanie:

sub my-sub {

#    try {              <--- remove this line...

        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;

#    }                  <--- ...and this one

}

sub other-sub { fail 'Failure_X' }

my-sub();

Użyłeś try. tryRobi kilka rzeczy, ale istotne jest tutaj to, że mówi Raku natychmiast promować żadnych FailureS w jego zakres wyjątków - czyli to, co mówisz, że nie chcesz. Więc najprostszym rozwiązaniem jest po prostu przestać to robić.


Ta odpowiedź po prostu werbalnie powtarza część wyjaśnień Jnthna (patrz w szczególności komentarze, które napisał poniżej swojej odpowiedzi). Ale nie byłem przekonany, że wszyscy czytelnicy zauważą / zrozumieją ten aspekt, i nie sądziłem, że komentarz lub dwa na temat odpowiedzi Jnthna pomogłyby, stąd ta odpowiedź.

Napisałem to jako odpowiedź społeczności, aby upewnić się, że nie skorzystam z żadnych głosów poparcia, ponieważ oczywiście nie jest to uzasadnione. Jeśli otrzyma wystarczającą liczbę głosów negatywnych, po prostu go usuniemy.

raiph
źródło