Unterscheiden Sie zwischen Ausnahme und Fehler in einem CATCH-Block [RAKU]

9

Wir wissen, dass ein Fehler von einem CATCH-Block behandelt werden kann.

Im folgenden Beispiel erstellen wir einen 'AdHoc'-Fehler (in einem anderen Sub) und behandeln die Ausnahme in einem CATCH-Block (in meinem 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();

Die Ausgabe ist die folgende:

AdHoc Exception handled here
This was a Failure

Meine Frage ist jedoch: Wie können wir zwischen Fehler und einer "normalen" Ausnahme im CATCH-Block unterscheiden, um zwischen den beiden Fällen zu unterscheiden?

Jakar
quelle

Antworten:

12

Die Beziehung zwischen Failureund Exceptionbesteht darin, dass a ein Failurehat Exception- das heißt, es enthält das Ausnahmeobjekt als Teil seines Zustands. Etwas wie das:

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

Wenn ein Failure"explodiert", tut es dies, indem es das wirft Exception, was sich darin befindet. Was also den CATCHBlock erreicht , ist das ExceptionObjekt, und es gibt keine Verbindung zurück zum Gehäuse Failure. (Tatsächlich könnte ein gegebenes ExceptionObjekt im Prinzip von vielen Failures gehalten werden.)

Daher gibt es keinen direkten Weg, dies zu erkennen. Aus gestalterischer Sicht sollten Sie es wahrscheinlich nicht sein und einen anderen Weg finden, um Ihr Problem zu lösen. A Failureist nur eine Möglichkeit, das Auslösen einer Ausnahme zu verschieben und zuzulassen, dass sie als Wert behandelt wird. Es ist nicht beabsichtigt, dass sich die Art des zugrunde liegenden Problems ändert, da es eher als Wert als als sofortige Übertragung des Kontrollflusses vermittelt wird. Leider wurde das ursprüngliche Ziel in der Frage nicht angegeben; Möglicherweise ist es hilfreich, sich Steuerungsausnahmen anzusehen, andernfalls stellen Sie möglicherweise eine andere Frage zu dem zugrunde liegenden Problem, das Sie lösen möchten. Es gibt wahrscheinlich einen besseren Weg.

Der Vollständigkeit halber werde ich beachten Sie, dass es indirekte Weise , dass man erkennen kann , dass die Exceptiondurch ein geworfen wurde Failure. Wenn Sie beispielsweise .backtracedas Ausnahmeobjekt abrufen und sich das Paket des oberen Frames ansehen, können Sie feststellen, dass es aus dem Failurefolgenden Verzeichnis stammt :

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

Dies hängt jedoch stark von Implementierungsdetails ab, die sich leicht ändern können, sodass ich mich nicht darauf verlassen würde.

Jonathan Worthington
quelle
Um die Dinge zu klären, ist es meine Absicht, nur Ausnahmen zu behandeln (im CATCH-Block). Im Falle eines Fehlers möchte ich fortfahren, als wäre nichts passiert, und den Rest des Codes (außerhalb von CATCH) den Fehler behandeln lassen. In meinem Beispiel habe ich nicht erwartet, dass der zurückgegebene Fehler die enthaltene Ausnahme auslöst! Ich habe nur das Ergebnis in $ b erhalten und es als Bool überprüft. Dies stellt meines Erachtens keine "Verwendung" des Fehlers und damit keine Auslösung des CATCH-Blocks dar! Stattdessen scheint der CATCH immer die im Fehler enthaltene Ausnahme zu behandeln !!
Jakar
Darüber hinaus hat in Ihrem Beispiel für die indirekte Methode zum Erkennen eines Fehlers der zurückgegebene Bool (von der intelligenten Überprüfung des Fehlertyps) den Wert "False". Aber ich habe erwartet, dass es "wahr" ist! Habe ich etwas verpasst???
Jakar
1
@jakar Ein tryBlock impliziert das use fatalPragma, was bedeutet, dass jeder Failurevon einem im Block getätigten Aufruf zurückgegebene sofort in eine Ausnahme umgewandelt wird. Nur nicht benutzen try; a CATCHkann in jeden Block in Raku gehen (also nur auf der Ebene von sub). Alternativ können Sie no fataloben in Ihren tryBlock schreiben .
Jonathan Worthington
Und was ist mit meinem zweiten Kommentar?
Jakar
1
Ausführen des Beispiels Ich habe Drucke Trueauf der Rakudo-Version gegeben, die ich lokal habe. Wenn dies bei Ihnen nicht der Fall ist, beweist dies nur, dass dies fragil ist.
Jonathan Worthington
6

Entfernen Sie einfach die tryHülle:

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();

Du hast benutzt try. A trymacht ein paar Dinge, aber das Relevante hier ist, dass es Raku anweist, alle Failures in seinem Umfang sofort zu Ausnahmen zu befördern - was Sie sagen, dass Sie nicht wollen. Die einfachste Lösung besteht also darin, einfach damit aufzuhören.


Diese Antwort wiederholt nur wörtlich einen Teil von jnthns Erklärung (siehe insbesondere Kommentare, die er unter seiner Antwort geschrieben hat). Aber ich war nicht davon überzeugt, dass alle Leser diesen Aspekt erkennen / verstehen würden, und dachte nicht, dass ein oder zwei Kommentare zu jnthns Antwort helfen würden, daher diese Antwort.

Ich habe dies als Community-Antwort geschrieben, um sicherzustellen, dass ich nicht von Upvotes profitiere, da dies offensichtlich nicht gerechtfertigt ist. Wenn es genug Downvotes gibt, werden wir es einfach löschen.

Raiph
quelle