1

I run Dialyzer on the following MVR piece of code which passes the test_function except for the IncorrectMap. Why is that the case when I deliberately return different PlayersMapNew for the case branches although they are specified to be the same. And how can I force Dialyzer to be more strict about my code, such that States are easier to manage keep track of.

My concern especially is if I have filled out the way of making a map type checking correctly , in order to make sure that it is Done in accordance with the documentation. The latter I think is a bit scarce. Can one actually check down to the individual key of a map , whether it is correct or not?

-module(example_module).

-export([my_function/1, state_changer/3, test_function/0]).

-type map_arg() :: #{key1 => integer(), key2 => atom()}.
-type move() :: rock | paper | scissor.
-type playersmap() :: #{players => {pid(), pid()}} | #{players => {pid(), pid()}, fst_mover => {{pid(),_}, move()}}.


-spec my_function(Map_arg :: map_arg()) -> ok.

my_function(Map) ->
    ok.

test_function() ->
    CorrectMap = #{key1 => 1, key2 => 'value'},
    IncorrectMap = #{key1 => 1, key2 => 2},
    my_function(CorrectMap),
    my_function(IncorrectMap),
    state_changer({move, rock}, {self(), tag}, #{players  => {self(), self()}, fst_mover => {{self(), tag}, scissor}}).


-spec state_changer({move, Choice :: move()}, From :: {pid(), _}, PlayersMap :: playersmap()) -> {reply, playersmap()}.
state_changer({move, Choice}, From, PlayersMap) ->        
    {CurId, _} = From,    
    {P1Id, P2Id} = maps:get(players, PlayersMap),
    case CurId of
        P1Id ->                        
            PlayersMapNew = maps:put(fst_mover, {From, Choice}, PlayersMap),                    
            {reply, PlayersMapNew};
        P2Id -> 
            PlayersMapNew = maps:put(fst_mover, {Choice, From}, PlayersMap),                    
            {reply, PlayersMapNew}
    end.
2240
  • 1,547
  • 2
  • 12
  • 30
Piskator
  • 605
  • 1
  • 9
  • 1
    I couldn't confirm if this works on this particular example, but adding the `no_missing_return` and `no_extra_return` [flags](https://www.erlang.org/doc/man/dialyzer.html) (added [since OTP25](https://www.erlang.org/news/157#dialyzer)) helps catching more type issues especially when working with `case`. That being said, dialyzer will never be able to catch all type issues and there are limitations to what success typing can see (see [explanation](https://learnyousomeerlang.com/dialyzer)). – sabiwara Jan 25 '23 at 23:18
  • In my case when I dialized the example I don't get an error but when I've been using a map for other type checking, dializer has been able to find the errors. My concern is if I have filled out the way of making a map type checking correctly , in order to make sure that it is Done in accordance with the documentation. The latter I think is a bit scarce. Can you actually check down to the individual key of a map , whether it is correct or not? – Piskator Jan 26 '23 at 06:45
  • Dialyzer is able to catch errors on map keys and values, in your case you can see it if you comment out the first clause of your case (`P1Id ->`). I'm then getting `Invalid type specification for function dial:state_changer/3. The success typing is ({'move', _}, {_, _}, map()) -> #{'fst_mover' := {_, {_, _}}, _ => _}`. I think it just tends to be too forgiving for conditionals, since it gives up once it found one successful clause (hence, success typing). The extra flags improve this in my experience though. – sabiwara Jan 26 '23 at 13:50
  • I have tried to change the first line as you suggest, but I am not able to reproduce the results. First of all chanign that line would also mean that the return type would have to be changed, and this time back to just `PlayersMap` rather than `PlayersMapNew`. – Piskator Jan 28 '23 at 10:58

0 Answers0