Distinguir entre excepción y falla en un bloque CATCH [RAKU]

9

Sabemos que una falla puede ser manejada por un bloque CATCH.

En el siguiente ejemplo creamos una falla 'AdHoc' (en otro sub) y manejamos la excepción en un bloque CATCH (en mi 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();

El resultado es el siguiente:

AdHoc Exception handled here
This was a Failure

Sin embargo, mi pregunta es: ¿cómo podemos distinguir entre el fracaso y una excepción "normal" en el bloque CATCH para diferenciar entre los dos casos?

jakar
fuente

Respuestas:

12

La relación entre Failurey Exceptiones que unFailure tiene un Exception- es decir, contiene el objeto de excepción como parte de su estado. Algo como esto:

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

Cuando un Failure"explota", lo hace arrojando el Exceptionque está dentro de él. Por lo tanto, lo que llega al CATCHbloque es el Exceptionobjeto, y no hay un enlace de regreso al recinto.Failure . (De hecho, un Exceptionobjeto dado podría ser mantenido en principio por muchos Failures.)

Por lo tanto, no hay una forma directa de detectar esto. Desde una perspectiva de diseño, probablemente no debería serlo, y debería encontrar una forma diferente de resolver su problema. A Failurees solo una forma de diferir el lanzamiento de una excepción y permitir que se trate como un valor; No se pretende que la naturaleza del problema subyacente cambie porque se transmite como un valor y no como una transferencia inmediata del flujo de control. Desafortunadamente, el objetivo original no se indicó en la pregunta; puede que le resulte útil observar las excepciones de control, pero de lo contrario, quizás publique otra pregunta sobre el problema subyacente que está tratando de resolver. Probablemente hay una mejor manera.

Para completar, voy a señalar que hay son formas indirectas que uno puede detectar que el Exceptionfue arrojado por una Failure. Por ejemplo, si obtiene el .backtraceobjeto de excepción y mira el paquete del marco superior, es posible determinar que proviene de Failure:

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

Sin embargo, esto depende en gran medida de los detalles de implementación que podrían cambiar fácilmente, por lo que no confiaría en ello.

Jonathan Worthington
fuente
Solo para aclarar las cosas, mi intención es manejar solo las excepciones (en el bloque CATCH). En caso de falla, quiero reanudar como si nada hubiera pasado y dejar que el resto del código (fuera de CATCH) maneje la falla. En mi ejemplo, ¡no esperaba que la falla devuelta activara la excepción contenida! Todo lo que hice fue obtener el resultado en $ b y verificarlo como Bool. ¡Eso, en mi opinión, no constituye el "uso" de la Falla y, por lo tanto, la activación del bloque CATCH! ¡En lugar de eso, parece que CATCH siempre maneja la excepción contenida en la falla!
jakar
Además, en su ejemplo, sobre la forma indirecta de detectar un Fallo, el valor Bool devuelto (desde la comprobación inteligente del Tipo de Fallo) es de valor "Falso". ¡Pero esperaba que fuera "Verdadero"! ¿¿¿Me he perdido algo???
jakar
1
@jakar Un trybloque implica el use fatalpragma, lo que significa que cualquier Failuredevolución de una llamada realizada en el bloque se convierte inmediatamente en una excepción. Simplemente no lo uses try; a CATCHpuede ir en cualquier bloque en Raku (así que solo tenlo al nivel de sub). Alternativamente, escriba no fatalen la parte superior de su trybloque.
Jonathan Worthington
¿Y qué hay de mi segundo comentario?
jakar
1
Ejecutando el ejemplo, di impresiones Trueen la versión Rakudo que tengo localmente. Si no lo hace en el suyo, eso solo prueba el punto sobre la fragilidad de hacer esto.
Jonathan Worthington
6

Simplemente retire el tryenvoltorio:

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

Que utilizó try. A tryhace algunas cosas, pero lo pertinente aquí es que le dice a Raku que promueva inmediatamente cualquier Failures en su alcance a excepciones, que es lo que usted dice que no quiere. Entonces, la solución más simple es dejar de hacerlo.


Esta respuesta simplemente repite parte de la explicación de jnthn (ver en particular los comentarios que escribió debajo de su respuesta). Pero no estaba convencido de que todos los lectores detectaran / entendieran este aspecto, y no pensé que un comentario o dos sobre la respuesta de jnthn ayudarían, de ahí esta respuesta.

He escrito esto como una respuesta de la comunidad para asegurarme de que no me beneficiaré de ningún voto positivo porque obviamente no lo garantiza. Si recibe suficientes votos negativos, lo eliminaremos.

raiph
fuente