[erlang-questions] Asserting exact maps

zxq9 zxq9@REDACTED
Wed Oct 11 11:20:20 CEST 2017


On 2017年10月11日 水曜日 08:47:40 Technion wrote:
> Hi,
> 
> 
> I'm wondering if there is a simple process I can use to verify a map
> only contains valid variables. Consider the following example:
> 
> 
> 2> Checkmaps = fun(M) ->
> 2> #{"one" := One, "two" := _Two} = M,
> 2>     One end.
> #Fun<erl_eval.6.99386804>
> 
> % These crash as expected
> 
> 5> Checkmaps(#{"test" => one }).
> ** exception error: no match of right hand side value #{"test" => one}
> 6> Checkmaps(#{"one" => one }).
> ** exception error: no match of right hand side value #{"one" => one}
> 
> 
> % This works as expected
> 
> 7> Checkmaps(#{"one" => one, "two" => two }).
> one
> 
> % This however also runs - I would like it to crash like the first example
> 
> 8> Checkmaps(#{"one" => one, "two" => two, "three" => test }).
> 
> one
> 
> 
> The use case here is I'm pulling external data - anything I'm not
> expecting is not a happy path. Any assistance appreciated.

I find it easiest (both to write and to prove) to convert the map to a
list and then perform a complete and exact comparison of keys over it.

If I still need the map version then I'll pass the original if it
survived, but almost every time I have an exact set of keys that must
exist (and be the only keys that exist) I actually need a record, not
a map (because the data is semantically a tuple, not a map of variable
{K, V} pairs). The only exception to this is when I am passing the data
on to some external lib that was written by the sort of person who
believes that there is only one data type in the universe.

Another easy to write and easy to prove approach is to incrementally
pack a structure by using maps:take/2, and checking whether the map is
empty at the end or not. If it is empty then your structure should be
passed forward and life goes on as expected.

Any variation of that will work fairly simply.


1> Checkmap =
1>     fun(Map,Keys) ->
1>         Drop = fun(K, M) -> {_, Next} = maps:take(K, M), Next end,
1>         0 = maps:size(lists:foldl(Drop, Map, Keys))
1>     end.
#Fun<erl_eval.12.87737649>
2> Checkmap(#{"one" => 1, "two" => 2}, ["one", "two"]).
0
3> Checkmap(#{"one" => 1, "two" => 2}, ["one", "two", "three"]).
** exception error: no match of right hand side value error
4> Checkmap(#{"one" => 1, "two" => 2}, ["one", "three"]).       
** exception error: no match of right hand side value error
5> Checkmap(#{"one" => 1, "two" => 2}, ["one"]).         
** exception error: no match of right hand side value 1

But I prefer the list version that operates over lists, personally.

-Craig



More information about the erlang-questions mailing list