Erlang Notes

dialyzer cheat sheet
Login

dialyzer cheat sheet

https://www.erlang.org/doc/reference_manual/typespec.html

making a PLT for all of OTP

The shipped applications are in versioned subdirectories of code:lib_dir():

$ dialyzer --build_plt --apps $(erl -noshell -eval '{ok, A0} = file:list_dir(code:lib_dir()), A1 = lists:map(fun (App) -> hd(string:split(App, "-")) end, A0), lists:foreach(fun (App) -> io:fwrite("~ts\n", [App]) end, A1), halt().')

using types from other modules

Prefix the type name, similarly to a function call. E.g., calendar:date()

export a function to keep dialyzer from narrowing its type to its actual usage

Consider:

-module(e5).
-export([main/0]).
-record(day, {
    date :: calendar:date() | undefined,
    goal :: number() | undefined,
    score :: number()
}).

initialized(#day{date = undefined}) -> false;
initialized(#day{goal = undefined}) -> false;
initialized(_) -> true.

main() ->
    erlang:display(initialized(#day{score = 2})).

Dialyzer will complain that the second pattern of initialized/1 can never match:

e5.erl:10:1: The pattern 
          {'day', _, 'undefined', _} can never match since previous clauses completely covered the type 
          #day{score :: 2}
e5.erl:11:1: The variable _ can never match since previous clauses completely covered the type 
          #day{score :: 2}

But note the type, #day{score :: 2}. Dialyzer has narrowed the type to its single use. When this function is exported dialyzer has no more complaints.

Of course, it's reasonable to not want to pollute a module's interface for such a reason. An easy alternative to add tests:

-include_lib("eunit/include/eunit.hrl").
initialized_test() ->
    false = initialized(#day{score = 0}),
    false = initialized(#day{date = {2024, 5, 16}, score = 0}),
    true = initialized(#day{date = {2024, 5, 16}, goal = 10, score = 0}).