aprendiendo ( Erlang ).

martes, 13 de septiembre de 2011

Pila de ejecución. Manejo de excepciones II

| 0 comentarios |

En el post anterior sobre try...catch vimos como utilizarlo y tratar los errores en Erlang. En este veremos algunos truquillos.

¿Se puede capturar todas las excepciones posibles?

Jugando con los ejemplos realizados en el post sobre try...catch me pregunte: ¿Se puede capturar todas las excepciones posibles?. La respuesta me lo darían las pruebas y me puse manos a la obra.
El primer planteamiento del problema me llevo a siguiente intento:
provocar_error(1) ->
    a;
provocar_error(2) ->
    throw(a);
provocar_error(3) ->
    exit(a);
provocar_error(4) ->
    {'EXIT', a};
provocar_error(5) ->
    erlang:error(a).

prueba1() ->
    [capturar1(I) || I <- [1,2,3,4,5]].

capturar1(N) ->
    try provocar_error(N) of
        Val -> {N, noCapturado, Val}
    catch
        X -> {N, capturado, X}
    end.

1> c(trycatch). {ok,trycatch} 2> trycatch:prueba1(). ** exception exit: a in function trycatch:provocar_error/1 in call from trycatch:capturar1/1 in call from trycatch:'-prueba1/0-lc$^0/1-0-'/1 in call from trycatch:'-prueba1/0-lc$^0/1-0-'/1 3> trycatch:capturar1(1). {1,noCapturado,a} 4> trycatch:capturar1(2). {2,capturado,a} 5> trycatch:capturar1(3). ** exception exit: a in function trycatch:provocar_error/1 in call from trycatch:capturar1/1 6> trycatch:capturar1(4). {4,noCapturado,{'EXIT',a}} 7> trycatch:capturar1(5). ** exception error: a in function trycatch:provocar_error/1 in call from trycatch:capturar1/1
Comprobando los resultados de la ejecución llegue a la conclusión de que sólo era posible capturar errores de tipo throw. Buuaaaaa que mier... Pero entonces, se me ocurrió otro planteamiento:
prueba2() ->
    [capturar2(I) || I <- [1,2,3,4,5]].

capturar2(N) ->
    try provocar_error(N) of
        Val -> {N, noCapturado, Val}
    catch
        T:X -> {N, capturado, T, X}
    end.

3> trycatch:prueba2(). [{1,noCapturado,a}, {2,capturado,throw,a}, {3,capturado,exit,a}, {4,noCapturado,{'EXIT',a}}, {5,capturado,error,a}]
TaaachaaAAANNNN! Así, que este es el truquillo. Pero, ¿Por qué funciona también el segundo ejemplo y no el primero? La respuesta no es trivial. Al parecer, en Erlang asumieron que, si no establecemos el tipo de error a capturar el sistema toma por defecto el tipo throw, es decir, que donde pusimos X (en el primer ejemplo), Erlang toma como patrón throw:X para el pattern matching.

Pila de ejecución

A veces, para conocer bien o averiguar el motivo del error necesitamos conocer la pila de ejecución. Ya hemos, visto, en try...catch, que perdíamos la traza o pila de ejecución. Para obtener esta información tenemos una función de sistema erlang:get_stacktrace/0 que nos devuelve una lista de tuplas con el siguiente formato: [{Módulo, Función, Aridad | Lista_Argumentos}]. Este formato de lista tiene la misión de determinar o identificar unívocamente la función que provoco el error.
provocar_error(6) ->
    erlang:error(a, [6]).

...

prueba3() ->
    [capturar3(I) || I <- [1,2,3,4,5,6]].

capturar3(N) ->
    try provocar_error(N) of
        Val -> {N, noCapturado, Val}
    catch
        T:X -> {N, capturado, T, X, erlang:get_stacktrace()}
    end.

4> trycatch:prueba3(). [{1,noCapturado,a}, {2,capturado,throw,a, [{trycatch,provocar_error,1}, {trycatch,capturar3,1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {erl_eval,do_apply,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}, {3,capturado,exit,a, [{trycatch,provocar_error,1}, {trycatch,capturar3,1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {erl_eval,do_apply,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}, {4,noCapturado,{'EXIT',a}}, {5,capturado,error,a, [{trycatch,provocar_error,1}, {trycatch,capturar3,1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {erl_eval,do_apply,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}, {6,capturado,error,a, [{trycatch,provocar_error,[6]}, {trycatch,capturar3,1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {trycatch,'-prueba3/0-lc$^0/1-0-',1}, {erl_eval,do_apply,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}]
Como reseña, destacar que he añadido una nueva clausula a la función trycatch:provocar_error/1 que permite lanzar un erlang:error/2 que tiene el formato: erlang:error(Motivo, Lista_argumentos). Esta función nos permite ver los argumentos, en la pila de llamadas, con los que se lanzo la función que provoco el error.
En los resultados de ejecución, podemos observar como nuestra función trycatch:provocar_error/1 es la encargada de provocar el error o lanzar la excepción, que fue llamada por la función trycatch:capturar3/1, que fue llamada por ..., etc, etc.

Publicar un comentario

0 comentarios:

 
Licencia Creative Commons
Aprendiendo Erlang por Verdi se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported.