[erlang-questions] Type def and spec syntax

Fred Hebert mononcqc@REDACTED
Mon Oct 3 13:16:29 CEST 2016

On 10/03, Robert Virding wrote:
>And it if can be used in a
>meaningful way why isn't it documented? I think that having syntax which
>can never legal is a great way to complicate things. Which we don't need.

The three samples for syntax were:

-type atom(X) :: list(X).
-spec foo(Y) -> integer() when atom(Y).
-spec foo(Y) -> integer() when atom(Y :: integer()).

So here's a valid way to use them that is useful, at the very least in 
some cases:

    -type collection(K, V) :: #{K => V}
                            | dict:dict(K, V)
                            | gb_trees:tree(K, V).
    -spec lookup(K,F,C) -> V when
                  C :: collection(K, V),
                  F :: fun((K, C) -> V).
    lookup(K, F, C) -> F(K, C).
    main() ->
        C = maps:from_list([{a,1},{b,2},{c,3}]),
        lookup(b, fun maps:get/2, C),
        lookup("bad ignored type", fun maps:get/2, C),
        lookup(b, C, fun maps:get/2).

This module defines an accessor function using the parametrized types 
and 'when' parts of typespecs syntax. Sadly the analysis is currently 
not good enough to figure out that "bad ignored type" is not an 
acceptable value of `K' for the lookup (it appears dialyzer does not do 
the parametrized inference this deep), but in the third call, it can 
definitely infer that I swapped the collection and the accessor 

    mod.erl:13: Function main/0 has no local return
    mod.erl:17: The call mod:lookup('b',C::#{'a'=>1, 'b'=>2, 
    'c'=>3},fun((_,_) -> any())) does not have a term of type 
    dict:dict(_,_) | gb_trees:tree(_,_) | map() (with opaque subterms) 
    as 3rd argument

Had I otherwise defined my spec as:

    -spec lookup(K,F,C) -> V when
                  K :: atom(),
                  C :: collection(K, V),
                  F :: fun((K, C) -> V).

Which adds a constraint that keys must be atoms, Then dialyzer would 
have caught my error:

    mod.erl:17: The call mod:lookup("bad ignored type",fun((_,_) -> 
    any()),C::#{'a'=>1, 'b'=>2, 'c'=>3}) breaks the contract (K,F,C) -> 
    V when K :: atom(), C :: collection(K,V), F :: 
    fun((K,collection(K,V)) -> V)

If you're using rebar3, you should also be getting colored rebar3 
output, which does make the error a lot easier to see by putting the bad 
value in red and the piece of contract it breaks in green*: 


* we should find a way to somehow parametrize the colors to help 
* colorblind users I guess.

More information about the erlang-questions mailing list