[erlang-questions] Dialyzer question

Tobias Lindahl tobias.lindahl@REDACTED
Tue Nov 15 09:12:48 CET 2011


2011/11/15 Steve Strong <steve@REDACTED>:
> 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.

The specs was intended to be used as the hinting mechanism for
Dialyzer. They are a bit limited in the type variable handling,
though. Since I haven't worked with Dialyzer for a couple of years,
i'll have to bounce the question to the current Dialyzer team.

Tobias

> Cheers,
> Steve
> --
> Steve Strong
> @srstrong
> Sent with Sparrow
>
> On Tuesday, 15 November 2011 at 08:23, Tobias Lindahl wrote:
>
> 2011/11/14 Steve Strong <steve@REDACTED>:
>
> 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 <steve@REDACTED> 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
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
>
>
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
>



More information about the erlang-questions mailing list