[erlang-questions] Dialyzer question

Anthony Ramine <>
Tue Nov 15 09:52:46 CET 2011


Why don't you write specs for this exported function yourself? This way Dialyzer wouldn't have to infer too general types and it would just check whether the function code can handle its associated spec.

Le 15 nov. 2011 à 08:46, Steve Strong a écrit :

> Thanks Tobias, that's very helpful.  From the work I'd done so far, I had a feeling that things like lists:map/2 must have been hard-coded within Dialyzer, since that was the only explanation I could come up with as to the differing behaviour.
> 
> Since passing functions around like this is pretty common practice, are there any plans to improve Dialyzer's inference capabilities in this area?  If not, would it be possible to have some annotation format (similar to -spec) that effectively say "in this context, I know that this function will return X" (i.e., open up access to the "hard-coded" bit where the knowledge of lists:map/2 resides)?  Right now, as far as Dialyzer is concerned, I have any()'s passing all over the place, so clearly its ability to give me any useful feedback is severely limited.
> 
> Cheers,
> 
> Steve
> 
> -- 
> Steve Strong
> @srstrong
> 
> Sent with Sparrow
> 
> On Tuesday, 15 November 2011 at 08:23, Tobias Lindahl wrote:
> 
>> 2011/11/14 Steve Strong <>:
>>> That makes some sense, and indeed if I just export test/0 then it does work
>>> nicely.  Alas, do_something is what I really want to be exporting and if I
>>> do so, then dialyzer stops warning me.  I completely understand that in
>>> isolation, dialyzer has to infer that do_something can return any() - I was
>>> hoping that it would adjust that inference when in the context of the
>>> caller, where the mapping function is constraining the return type.
>> 
>> No such specializations happens for exported functions. If the
>> inference was stronger, we could find a type for do_something that
>> connected the type of the fun to the return type of the function using
>> type variables, but this is beyond Dialyzer. On the other hand, if the
>> function is not exported Dialyzer will propagate the union of the call
>> types, which in your case is just the single fun. That is why it finds
>> the specialized type in your case.
>> 
>>> Again, this seems analogous to lists:map/2, which is exported and yet
>>> dialyzer seems to do "the right thing".
>> 
>> There is hard-coded information in Dialyzer about lists:map/2 and
>> other commonly used library functions.
>> 
>> Tobias
>> 
>>> Confused :)
>>> --
>>> Steve Strong
>>> @srstrong
>>> Sent with Sparrow
>>> 
>>> On Monday, 14 November 2011 at 11:58, Joe Armstrong wrote:
>>> 
>>> Change export_all to
>>> 
>>> -export([test/0]).
>>> 
>>> Then you'll get this:
>>> 
>>> test.erl:8: Function test/0 has no local return
>>> test.erl:9: The pattern {'rec2', _} can never match the type #rec1{}
>>> 
>>> With export_all the arguments to do_something are totally unconstrained and
>>> can come from
>>> any wild place - so the inferred types are too permissive.
>>> 
>>> With export_all typer infers the following:
>>> 
>>>> typer test.erl
>>> %% File: "test.erl"
>>> %% ----------------
>>> -spec test() -> #rec2{}.
>>> -spec do_something(_,fun((_) -> any())) -> any().
>>> -spec map(_) -> #rec1{}.
>>> 
>>> The (.... any()) -> any() is because the second argument of do_something can
>>> come from anywhere
>>> so there are no type errors
>>> 
>>> But with -export([test/0]). we get this:
>>> 
>>> -spec test() -> none().
>>> -spec do_something(1,fun((_) -> #rec1{})) -> #rec1{}.
>>> -spec map(_) -> #rec1{}.
>>> 
>>> 
>>> /Joe
>>> 
>>> 
>>> 
>>> 
>>> On Mon, Nov 14, 2011 at 11:40 AM, Steve Strong <> wrote:
>>> 
>>> Hi All,
>>> I have a question on the behaviour of Dialyzer with higher-order functions.
>>>  Consider the following trivial code:
>>> -module(test).
>>> -compile(export_all).
>>> -record(rec1, {fred}).
>>> -record(rec2, {harry}).
>>> test() ->
>>>     #rec2{} = do_something(1, fun map/1).
>>> do_something(Num, MapFun) ->
>>>     MapFun(Num).
>>> map(_X) ->
>>>     #rec1{}.
>>> If I run Dialyzer against it, I get no errors or warnings logged, even
>>> though it's quite apparent that it will fail.  I would have expected that
>>> Dialyzer could infer that the return type of do_something was the return
>>> type of MapFun;  within the context of do_something(), that is clearly
>>> any(), but within the context of test() I would have thought that it could
>>> see that do_something() is actually going to return a #rec1{} record, that
>>> being the return type of map().
>>> If I replace the call to MapFun with a call to map(), as in:
>>> do_something(Num, _MapFun) ->
>>>     map(Num).
>>> Then everything works as expected - Dialyzer correctly tells me that my code
>>> is broken.  Is there anything I can do to "teach" dialyzer how to interpret
>>> the MapFun?  It works as I would like with functions such as lists:map():
>>> -module(test).
>>> -compile(export_all).
>>> -record(rec1, {fred}).
>>> -record(rec2, {harry}).
>>> test() ->
>>>     [#rec2{}] = lists:map(fun map/1, [1]).
>>> map(_X) ->
>>>     #rec1{}.
>>> With the above, I get the expected warning.  I've looked at lists.erl, and
>>> it doesn't look to be doing anything different.  It does have some -spec
>>> directives, but adding those in my code did not appear to help.
>>> Any hints?
>>> Cheers,
>>> Steve
>>> --
>>> Steve Strong
>>> @srstrong
>>> Sent with Sparrow
>>> 
>>> _______________________________________________
>>> erlang-questions mailing list
>>> 
>>> http://erlang.org/mailman/listinfo/erlang-questions
>>> 
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> erlang-questions mailing list
>>> 
>>> http://erlang.org/mailman/listinfo/erlang-questions
> 
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions

-- 
Anthony Ramine / @nokusu
Dev:Extend — http://dev-extend.eu/
So as I pray, “Unlimited Erlang Works”




More information about the erlang-questions mailing list