librelist archives

« back to archive

Dialyzer y Módulos Parametrizados

Dialyzer y Módulos Parametrizados

From:
Fernando Benavides
Date:
2010-10-01 @ 13:50
Hola muchachos, ¿qué tal?
Siendo uno de los (creo) pocos fans de dialyzer, ayer me topé con un
lindo problemita que probablemente requiera una solución creativa.  A
ver qué opinan...

Yo sé que desde la versión 2.2.0, Dialyzer soporta módulos
parametrizados, al menos así lo dicen sus "release notes".  Así que,
siendo que tengo la versión 2.3.1, me dispuse a probarlo...

Digamos entonces que, por alguna razón misteriosa (o_O), me encuentro
con un módulo parametrizado para trabajar con colas de atoms (Es sólo de
ejemplo, no vale preguntarse si esto tiene sentido en la vida real)...
Muy bien, ahí va el módulo entonces:
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        -module(atom_queue, [Queue]). 
        
        -export([push/1, pop/0]). 
        
        pop() -> case Queue of 
                   [] -> {undefined, atom_queue:new([])}; 
                   [X|Rest] -> {X, atom_queue:new(Rest)} 
                 end. 
        
        push(X) -> atom_queue:new(lists:reverse([X|Queue])). 
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Probablemente si lo compilo y luego ejecuto dialyzer no reciba ningún
warning, pero eso es porque no puse ninguna especificación (spec) en el
código y no lo estoy compilando con los warnings activados.  Ahora bien,
si al compilarlo uso la opción with warn_missing_spec, el compilador me
dirá que me olvidé de especificar las funciones pop/0 y push/1.  Así
que, les agrego las correspondientes specs:
 
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        -module(atom_queue, [Queue]). 
        
        -export([push/1, pop/0]). 
        
        -spec pop() -> {undefined | atom(), {?MODULE, [atom()]}}. 
        pop() -> case Queue of 
                   [] -> {undefined, atom_queue:new([])}; 
                   [X|Rest] -> {X, atom_queue:new(Rest)} 
                 end. 
        
        -spec push(atom()) -> {?MODULE, [atom()]}. 
        push(X) -> atom_queue:new(lists:reverse([X|Queue])). 
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Ahora, al compilar, no recibo más warnings.  El problema es que al
ejecutar dialyzer aparecen dos:
        Contract for function that does not exist: atom_queue:pop/0 
        Contract for function that does not exist: atom_queue:push/1 
        
Si yo quisiera que dialyzer no se quejara, debería modificar los specs
de la siguiente manera:
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        -module(atom_queue, [Queue]). 
        
        -export([push/1, pop/0]). 
        
        -spec pop({?MODULE, [atom()]}) -> {undefined | atom(), {?MODULE,
[atom()]}}.
        pop() -> case Queue of 
                   [] -> {undefined, atom_queue:new([])}; 
                   [X|Rest] -> {X, atom_queue:new(Rest)} 
                 end. 
        
        -spec push(atom(), {?MODULE, [atom()]}) -> {?MODULE, [atom()]}. 
        push(X) -> atom_queue:new(lists:reverse([X|Queue])). 
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Pero claro, al tratar de compilar ese código, el compilador presenta dos
errores (ya ni siquiera son warnings):
        spec for undefined function atom_queue:pop/1
        spec for undefined function atom_queue:push/2 

Así que, en definitiva, mi pregunta es: ¿Cómo puedo hacer para compilar
mi módulo parametrizado con specs sin generar errores de compilación ni
warnings por parte del dialyzer?

                                                                        
________________________________________________________________________
                                                      Fernando Benavides

                                              fernando@inakanetworks.com

Re: [erlar] Dialyzer y Módulos Parametrizados

From:
Juan Jose Comellas
Date:
2010-10-01 @ 14:26
Creo que la respuesta simple es que dialyzer no soporta completamente los
tipos parametrizados todavía. Por ahí deberías mandar un mail a la lista de
Erlang a ver qué contesta Kostis Sagonas, que es el líder del equipo que
hizo el dialyzer, si no me equivoco.


2010/10/1 Fernando Benavides <fernando.benavides@inakanetworks.com>

>  Hola muchachos, ¿qué tal?
> Siendo uno de los (creo) pocos fans de dialyzer, ayer me topé con un lindo
> problemita que probablemente requiera una solución creativa.  A ver qué
> opinan...
>
> Yo sé que desde la versión 2.2.0, Dialyzer soporta módulos parametrizados,
> al menos así lo dicen sus "release notes".  Así que, siendo que tengo la
> versión 2.3.1, me dispuse a probarlo...
>
> Digamos entonces que, por alguna razón misteriosa (o_O), me encuentro con
> un módulo parametrizado para trabajar con colas de atoms (Es sólo de
> ejemplo, no vale preguntarse si esto tiene sentido en la vida real)...
> Muy bien, ahí va el módulo entonces:
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>         -module(atom_queue, [Queue]).
>
>         -export([push/1, pop/0]).
>
>         pop() -> case Queue of
>                    [] -> {undefined, atom_queue:new([])};
>                    [X|Rest] -> {X, atom_queue:new(Rest)}
>                  end.
>
>         push(X) -> atom_queue:new(lists:reverse([X|Queue])).
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>
> Probablemente si lo compilo y luego ejecuto dialyzer no reciba ningún warning,
> pero eso es porque no puse ninguna especificación (spec) en el código y no
> lo estoy compilando con los warnings activados.  Ahora bien, si al
> compilarlo uso la opción with warn_missing_spec, el compilador me dirá que
> me olvidé de especificar las funciones pop/0 y push/1.  Así que, les agrego
> las correspondientes specs:
>
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>         -module(atom_queue, [Queue]).
>
>         -export([push/1, pop/0]).
>
>         *-spec pop() -> {undefined | atom(), {?MODULE, [atom()]}}. *
>         pop() -> case Queue of
>                    [] -> {undefined, atom_queue:new([])};
>                    [X|Rest] -> {X, atom_queue:new(Rest)}
>                  end.
>
>         *-spec push(atom()) -> {?MODULE, [atom()]}. *
>         push(X) -> atom_queue:new(lists:reverse([X|Queue])).
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>
> Ahora, al compilar, no recibo más warnings.  El problema es que al ejecutar
> dialyzer aparecen dos:
>         Contract for function that does not exist: atom_queue:pop/0
>         Contract for function that does not exist: atom_queue:push/1
>
> Si yo quisiera que dialyzer no se quejara, debería modificar los specs de
> la siguiente manera:
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>         -module(atom_queue, [Queue]).
>
>         -export([push/1, pop/0]).
>
>         -spec pop(*{?MODULE, [atom()]}*) -> {undefined | atom(), {?MODULE,
> [atom()]}}.
>         pop() -> case Queue of
>                    [] -> {undefined, atom_queue:new([])};
>                    [X|Rest] -> {X, atom_queue:new(Rest)}
>                  end.
>
>         -spec push(atom(), *{?MODULE, [atom()]}*) -> {?MODULE, [atom()]}.
>         push(X) -> atom_queue:new(lists:reverse([X|Queue])).
>         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>
> Pero claro, al tratar de compilar ese código, el compilador presenta dos
> errores (ya ni siquiera son warnings):
>         spec for undefined function atom_queue:pop/1
>         spec for undefined function atom_queue:push/2
>
> Así que, en definitiva, mi pregunta es: ¿Cómo puedo hacer para compilar mi
> módulo parametrizado con specs sin generar errores de compilación ni
> warnings por parte del dialyzer?
>
>   ------------------------------
>
> *Fernando Benavides <http://google.com/profiles/greenmellon>*
>
> *fernando@inakanetworks.com*
>

Re: [erlar] Dialyzer y Módulos Parametrizados

From:
Mariano Guerra
Date:
2010-10-01 @ 14:18
2010/10/1 Fernando Benavides <fernando.benavides@inakanetworks.com>
>
> Hola muchachos, ¿qué tal?
> Siendo uno de los (creo) pocos fans de dialyzer, ayer me topé con un 
lindo problemita que probablemente requiera una solución creativa.  A ver 
qué opinan...
>
> Así que, en definitiva, mi pregunta es: ¿Cómo puedo hacer para compilar 
mi módulo parametrizado con specs sin generar errores de compilación ni 
warnings por parte del dialyzer?

es raro, debe ser por la magia que se usa para hacer modulos
parametrizados. este es el module_info del modulo:

1> atom_queue:module_info().
[{exports,[{new,1},
           {instance,1},
           {pop,1},
           {push,2},
           {module_info,0},
           {module_info,1}]},

asi que efectivamente la aridad es pop/1 y push/2. Como veo que ya
sabes el modulo parametrizado agrega una tupla como utlimo argumento
que contiene el estado del modulo.

el problema es que a alto nivel pop es pop/0 y push/1:

compilando con -P:

-export([push/1,pop/0]).

pop() ->
    case Queue of
        [] ->
            {undefined,atom_queue:new([])};
        [X|Rest] ->
            {X,atom_queue:new(Rest)}
    end.

push(X) ->
    atom_queue:new(lists:reverse([X|Queue])).

compilando con -E

pop({_,Queue} = THIS) ->
    case Queue of
        [] ->
            {undefined,atom_queue:new([])};
        [X|Rest] ->
            {X,atom_queue:new(Rest)}
    end.

push(X, {_,Queue} = THIS) ->
    atom_queue:new(lists:reverse([X|Queue])).


como ves le agrega magicamente el argumento pero a un mas bajo nivel,
puede que dializer haga el analisis en un nivel y se confunda.

PD: no te estoy ayudando en mucho, solo pienso en voz alta mientras
analizo el problema :P