Why Erlang Dialyzer cannot find type error in the following code? -
free_vars_in_dterm({var, v}) -> {var, v};
cannot type check, however, dialyzer says ok. $ dialyzer *erl checking whether plt ~/.dialyzer_plt up-to-date... yes proceeding analysis... done in 0m0.66s done (passed successfully)
code below:
-module(formulas). -type formulas() :: {predicate(), [dterm()]} | {'~', formulas()} | {'&', formulas(), formulas()} | {'|', formulas(), formulas()} | {'->', formulas(), formulas()} | {'<->', formulas(), formulas()} | {'exist', variable(), formulas()} | {'any', variable(), formulas()}. -type dterm() :: constant() | variable() | {func(), [dterm()]}. -type constant() :: {const, atom()} | {const, integer()}. -type variable() :: {var, atom()}. -type func() :: {function, function_name::atom(), arity::integer()}. -type predicate() :: {predicate, predicate_name::atom(), arity::integer()}. -include_lib("eunit/include/eunit.hrl"). -export([is_ground/1, free_vars/1, is_sentence/1]). -spec is_ground(formulas()) -> boolean(). is_ground({_, {var, _}, _}) -> false; is_ground({{predicate, _, _}, terms}) -> lists:all(fun is_ground_term/1, terms); is_ground(formulas) -> ts = tuple_size(formulas), lists:all(fun is_ground/1, [element(i, formulas) || <- lists:seq(2, ts)]). -spec is_ground_term(dterm()) -> boolean(). is_ground_term({const, _}) -> true; is_ground_term({var, _}) -> false; is_ground_term({{function, _, _}, terms}) -> lists:all(fun is_ground_term/1, terms). -spec is_sentence(formulas()) -> boolean(). is_sentence(formulas) -> sets:size(free_vars(formulas)) =:= 0. -spec free_vars(formulas()) -> sets:set(variable()). free_vars({{predicate, _, _}, dterms}) -> join_sets([free_vars_in_dterm(dterm) || dterm <- dterms]); free_vars({_qualify, {var, v}, formulas}) -> sets:del_element({var, v}, free_vars(formulas)); free_vars(compound_formulas) -> tp_size = tuple_size(compound_formulas), join_sets([free_vars(element(i, compound_formulas)) || <- lists:seq(2, tp_size)]). -spec free_vars_in_dterm(dterm()) -> sets:set(variable()). free_vars_in_dterm({const, _}) -> sets:new(); free_vars_in_dterm({var, v}) -> {var, v}; free_vars_in_dterm({{function, _, _}, dterms}) -> join_sets([free_vars_in_dterm(dterm) || dterm <- dterms]). -spec join_sets([sets:set(t)]) -> sets:set(t). join_sets(set_list) -> lists:foldl(fun (t, acc) -> sets:union(t, acc) end, sets:new(), set_list).
first short answer, using dialyzer's maxims :
- dialyzer never wrong. (recited erlang programmers)
- dialyzer never promised find errors in code. (not famous)
maxim number 2 (admittedly not satisfying) "standard" answer "why dialyzer didn't catch error" question.
more explanatory answer:
dialyzer's analysis return values of functions doing over-approximations. result, value included in types should considered "maybe returned" value. has unfortunate side-effect values going returned (such {var, v}
tuple) considered "maybe returned". dialyzer must guarantee maxim 1 (never wrong), in cases unexpected value "may returned" not give warning, unless none of specified values can returned. last part checked @ level of entire function, , since in example of clauses indeed return sets, no warnings generated.
Comments
Post a Comment