[erlang-questions] Dialyzer confused by several clauses?

Tobias Lindahl tobias.lindahl@REDACTED
Mon Aug 18 10:44:19 CEST 2008


Paul,

Paul Guyot wrote:
> Hello,
> 
> Updating some code, I realized that dialyzer did not complain about an 
> incorrect specification.
> 
> Here is a simple code that it accepts:
> 
> -type(dict() :: any()).
> 
> -spec(clause/2::(integer(), dict()) -> tuple()).
> clause(0, Dict) ->
>     case orddict:find(0, Dict) of
>         {ok, Value} -> Value;
>         error -> 0.0
>     end;
> clause(Integer, Dict) ->
>     case orddict:find(Integer, Dict) of
>         {ok, Value} -> Value / Integer;
>         error -> 0.0
>     end.
> 
> The real contract is:
> -spec(clause/2::(integer(), dict()) -> float()).
> 
> An implied contact would be:
> -spec(clause/2::(integer(), dict()) -> float() | any()).
> 
> But this contract is certainly incompatible with the code:
> -spec(clause/2::(integer(), dict()) -> tuple()).
> 
> Am I doing something wrong or is it a known limitation?

The checking in Dialyzer whether a contract is valid is not as 
sophisticated as it could be. First it finds the success typing of a 
function and then it compares it with the contract. The default is to 
complain only if the two are contradicting.

In your case the return type in the success typing is any() because of 
the Value in the first clause (*). Since this does not contradict the 
tuple() in the contract, Dialyzer is satisfied. The reason for this 
somewhat liberal reasoning is the principle of sound warnings; if there 
is a possibility that a contract is correct then Dialyzer gives no 
warning. However, we could of course lift this restriction by allowing 
the user to give some option, but this is not present today.

There are options for getting more strict warnings about contracts. 
However, they would not have helped you in this case. The options are 
--Wunderspecs, --Woverspecs and --Wspecdiffs. Basically these options 
makes Dialyzer warn when the specs differ from the success typings of 
functions. They are not on by default since the resulting warnings are a 
bit confusing at times.

Tobias

(*) As you say, the type could be written as float() | any(), but unions 
must be disjoint so this is collapsed to any().



More information about the erlang-questions mailing list