简体   繁体   English

为什么Erlang Dialyzer在以下代码中找不到类型错误?

[英]Why Erlang Dialyzer cannot find type error in the following code?

free_vars_in_dterm({var, V}) -> {var, V}; obviously cannot type check, however, dialyzer says everything is OK. 显然不能打字检查,但是,透析器说一切都好。

$ dialyzer *erl
  Checking whether the PLT ~/.dialyzer_plt is up-to-date... yes
  Proceeding with analysis... done in 0m0.66s
done (passed successfully)

Code is as 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)
                                || I <- 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))
               || I <- 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 a short answer, using Dialyzer's maxims : 首先简单回答,使用Dialyzer的格言:

  1. Dialyzer is never wrong. 透析器永远不会错。 (recited very often by Erlang programmers) (经常被Erlang程序员朗诵)
  2. Dialyzer never promised to find all errors in your code. Dialyzer从未承诺在您的代码中找到所有错误。 (not so famous) (不那么有名)

Maxim number 2 is the (admittedly not very satisfying) "standard" answer to any "Why Dialyzer didn't catch this error" question. 对于任何“为什么Dialyzer没有发现这个错误”的问题,Maxim编号2是(当然不是很令人满意)“标准”答案。


More explanatory answer: 更多解释性答案:

Dialyzer's analysis for return values of functions is frequently doing over-approximations. Dialyzer对函数返回值的分析经常进行过近似。 As a result, any value included in the types should be considered to be a "maybe returned" value. 因此,类型中包含的任何值都应被视为“可能返回”的值。 This has the unfortunate side-effect that sometimes values that are certainly going to be returned (such as your {var, V} tuple) are also considered "maybe returned". 这有一个令人遗憾的副作用,有时候肯定要返回的值(例如你的{var, V}元组)也被认为是“可能返回”。 Dialyzer must guarantee maxim 1 (never be wrong), so in cases where an unexpected value "may be returned" it will not give a warning, unless none of the actually specified values can be returned. Dialyzer必须保证格言1(永远不会出错),因此在可能返回意外值的情况下,它不会发出警告,除非不能返回任何实际指定的值。 This last part is checked at the level of the entire function, and since in your example some of the clauses indeed return sets, no warnings are generated. 最后一部分在整个函数的级别进行检查,因为在您的示例中,某些子句确实返回集合,因此不会生成警告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM