Pregunta ¿Qué hay de malo en las excepciones en Perl?


Una discusión en otra pregunta me pregunto: ¿qué tienen los sistemas de excepción de otros lenguajes de programación que Carece de Perl?

Las excepciones incorporadas de Perl son un poco ad hoc en el sentido de que, como el sistema de objetos Perl 5, se atornillaron como una idea de último momento y sobrecargaron otras palabras clave (eval y die) que no están dedicados específicamente a excepciones.

La sintaxis puede ser un poco fea, en comparación con los lenguajes con sintaxis incorporada try / throw / catch type. Normalmente lo hago así:

eval { 
    do_something_that_might_barf();
};

if ( my $err = $@ ) { 
    # handle $err here
}

Hay varios módulos de CPAN que proporcionan azúcar sintáctico para agregar palabras clave de prueba / captura y para permitir la fácil declaración de jerarquías de clase de excepción y otras cosas.

El principal problema que veo con el sistema de excepción de Perl es el uso de $@ para contener el error actual, en lugar de un error catchtipo de mecanismo que podría ser más seguro, desde una perspectiva de alcance, aunque nunca me he encontrado personalmente con ningún problema $@ ser mordido


28
2018-01-29 21:06


origen


Respuestas:


Algunas clases de excepción, p. Error, no puede manejar el control de flujo desde dentro de los bloques try / catch. Esto conduce a errores sutiles:

use strict; use warnings;
use Error qw(:try);

foreach my $blah (@somelist)
{
    try
    {
        somemethod($blah);
    }
    catch Error with
    {
        my $exception = shift;
        warn "error while processing $blah: " . $exception->stacktrace();
        next;    # bzzt, this will not do what you want it to!!!
    };

    # do more stuff...
}

La solución consiste en utilizar una variable de estado y verificar que fuera del bloque try / catch, que para mí se ve horriblemente como el código stinky n00b.

Otros dos "errores" en el error (los cuales me causaron dolor ya que son horribles de depurar si no se ha encontrado con esto antes):

use strict; use warnings;
try
{
    # do something
}
catch Error with
{
    # handle the exception
}

Parece sensato, ¿verdad? Este código compila, pero conduce a errores extraños e impredecibles. Los problemas son:

  1. use Error qw(:try) fue omitido, entonces el try {}... bloque será mal interpretado (puede o no ver una advertencia, dependiendo del resto de su código)
  2. ¡falta el punto y coma después del bloque catch! Intuitivas como bloques de control no usan punto y coma, pero de hecho try es un llamada de método prototipado.

Oh sí, eso también me recuerda eso porque try, catch etc. son llamadas de método, lo que significa que la pila de llamadas dentro de esos bloques no será la esperada. (En realidad, hay dos niveles de pila adicionales debido a una llamada interna dentro de Error.pm.) En consecuencia, tengo algunos módulos llenos de código repetitivo como este, que solo agrega desorden:

my $errorString;
try
{
    $x->do_something();
    if ($x->failure())
    {
        $errorString = 'some diagnostic string';
        return;     # break out of try block
    }

    do_more_stuff();
}
catch Error with
{
    my $exception = shift;
    $errorString = $exception->text();
}
finally
{
    local $Carp::CarpLevel += 2;
    croak "Could not perform action blah on " . $x->name() . ": " . $errorString if $errorString;
};

13
2018-02-04 19:34



Prueba :: Tiny (o los módulos que se construyen sobre él) es la única forma correcta de tratar las excepciones en Perl 5. Las cuestiones involucradas son sutiles, pero el artículo vinculado las explica en detalle.

He aquí cómo usarlo:

use Try::Tiny;

try {
    my $code = 'goes here';
    succeed() or die 'with an error';
}
catch {
    say "OH NOES, YOUR PROGRAM HAZ ERROR: $_";
};

eval y $@ son partes móviles con las que no debes preocuparte.

Algunas personas piensan que esto es un desafío, pero después de haber leído las implementaciones de otros lenguajes (así como Perl 5), no es diferente de cualquier otro. Solo está el $@ parte móvil que puede atrapar su mano ... pero como con otras piezas de maquinaria con partes móviles expuestas ... si no lo toca, no le arrancará los dedos. Entonces use Try :: Tiny y mantenga su velocidad de escritura;)


24
2018-01-30 05:28



El método típico que la mayoría de la gente ha aprendido para manejar excepciones es vulnerable a las excepciones atrapadas que faltan:

eval { some code here };
if( $@ ) {  handle exception here };

Tu puedes hacer:

eval { some code here; 1 } or do { handle exception here };

Esto protege de la ausencia de la excepción debido a $@ siendo golpeado, pero aún es vulnerable a perder el valor de $@.

Para asegurarse de no criticar una excepción, cuando realiza su evaluación, debe localizar $@;

eval { local $@; some code here; 1 } or do { handle exception here };

Todo esto es una rotura sutil, y la prevención requiere mucho palabreo esotérico.

En la mayoría de los casos, esto no es un problema. Pero me he quemado por la excepción de comer destructores de objeto en código real. La depuración del problema fue horrible.

La situación es claramente mala. Mire todos los módulos en CPAN construidos proporcionan un manejo de excepción decente.

Respuestas abrumadoras a favor de Prueba :: Tiny combinado con el hecho de que Try :: Tiny no es "demasiado inteligente a la mitad", me han convencido para probarlo. Cosas como Trata de atraparlo y Excepción :: Clase :: TryCatch, Error, y así sucesivamente son demasiado complejas para que pueda confiar. Try :: Tiny es un paso en la dirección correcta, pero todavía no tengo una clase de excepción ligera para usar.


23
2018-01-30 00:08



Un problema que encontré recientemente con el eval mecanismo de excepción tiene que ver con el $SIG{__DIE__} entrenador de animales. He asumido, erróneamente, que este manejador solo recibe una llamada cuando el intérprete de Perl sale por die() y quería usar este controlador para registrar eventos fatales. Luego resultó que estaba registrando excepciones en el código de la biblioteca como errores fatales que claramente estaban equivocados.

La solución fue verificar el estado de la $^S o $EXCEPTIONS_BEING_CAUGHT variable:

use English;
$SIG{__DIE__} = sub {
    if (!$EXCEPTION_BEING_CAUGHT) {
        # fatal logging code here
    }
};

El problema que veo aquí es que el __DIE__ controlador se utiliza en dos situaciones similares pero diferentes. Ese $^S variable parece un add-on tardío para mí. Sin embargo, no sé si este es realmente el caso.


8
2018-01-30 15:46



Con Perl, el lenguaje y las excepciones escritas por el usuario se combinan: ambos establecen $@. En otros idiomas, las excepciones de idioma son independientes de las excepciones escritas por el usuario y crean un flujo completamente independiente.

Puede ver la base de excepciones escritas por el usuario.

Si hay My::Exception::one y My::Exception::two

if ($@ and $@->isa('My::Exception'))

atrapará a ambos.

Recuerde capturar cualquier excepción que no sea de usuario con un else.

elsif ($@)
    {
    print "Other Error $@\n";
    exit;
    }

También es bueno para envolver la excepción en una sub llamada al sub para lanzarlo.


2
2018-01-29 22:38



En C ++ y C #, puede definir tipos que se pueden lanzar, con bloques catch independientes que administran cada tipo. Los sistemas de tipo Perl tienen ciertos problemas molestos relacionados con RTTI y herencia, según lo que leo en el blog de chomatic.

No estoy seguro de cómo otros lenguajes dinámicos manejan excepciones; tanto C ++ como C # son lenguajes estáticos y eso conlleva una cierta potencia en el sistema de tipos.

los filosófico El problema es que las excepciones de Perl 5 están atornilladas; no están construidos desde el comienzo del diseño del lenguaje como algo integral de cómo se escribe Perl.


1
2018-01-29 21:11



Ha pasado mucho tiempo desde que utilicé Perl, por lo que mi memoria puede ser borrosa y / o Perl puede haber mejorado, pero por lo que recuerdo (en comparación con Python, que uso a diario):

  1. dado que las excepciones son una adición tardía, no son consistentemente compatibles en las bibliotecas centrales

    (No es cierto; no se admiten sistemáticamente en las bibliotecas principales porque a los programadores que escribieron esas bibliotecas no les gustan las excepciones).

  2. no hay una jerarquía de excepciones predefinida: no puede atrapar un grupo relacionado de excepciones atrapando la clase base

  3. no hay un equivalente de try: ... finally: ... para definir el código que se invocará independientemente de si se generó o no una excepción, p. para liberar recursos.

    (finally en Perl es en gran parte innecesario: los destructores de objetos se ejecutan inmediatamente después de la salida del alcance; no siempre que haya presión de memoria. Así que puedes desasignar cualquier recurso que no sea de memoria en tu destructor, y funcionará sana y salva).

  4. (por lo que yo sé) solo puedes lanzar cadenas: no puedes lanzar objetos que tengan información adicional

    (Completamente falso. die $object funciona tan bien como die $string.)

  5. no puede obtener un seguimiento de pila que le muestra dónde se lanzó la excepción: en Python obtiene información detallada, incluido el código fuente de cada línea en la pila de llamadas

    (Falso. perl -MCarp::Always y disfrutar.)

  6. es un kludge feo.

    (Subjetivo. Se implementa de la misma manera en Perl que en cualquier otro sitio. Solo usa palabras clave con nombres diferentes).


1
2018-01-29 21:34



No use Excepciones para errores regulares. Solo los problemas fatales que detendrán la ejecución actual deberían morir. Todos los demás deben ser manejados sin die.

Ejemplo: Validación de parámetros del sub llamado: No muera en el primer problema. Compruebe todos los otros parámetros y luego decida detenerse devolviendo algo o avisando y corrigiendo los parámetros defectuosos y proceda. Que hacer en modo prueba o desarrollo. Pero posiblemente die en modo de producción. Deja que la aplicación decida esto.

JPR (mi inicio de sesión de CPAN)

Saludos desde Sögel, Alemania


0
2018-01-01 18:54