View Source gradualizer (gradualizer v0.2.0)

Gradualizer is a static type checker for Erlang with support for gradual typing.

A static type checker detects errors thanks to type analysis of a given program. Gradualizer can infer some types, but mostly relies on function specs. In a nutshell, we could say that it checks for consistency of a function body with its declared spec and for consistency of a callee's spec with passed in arguments. Specifically, Gradualizer does not perform entire program flow analysis.

Gradual typing means that full type information is not required to raise warnings - any type information is better than no type information. It allows for a _gradual_ transition between fully static typing, which offers the best correctness guarantee, and fully dynamic typing, which means the least overhead of writing specs, the full Erlang expressiveness, and _letting it crash_ at runtime. We can choose the right balance between the static/dynamic extremes depending on the application, project maturity, team size, the need for reusability or for documentation consistency.

If you are interested in using Gradualizer to type check your code, then you might be looking for the Rebar3 plugin rebar_prv_gradualizer or the command-line interface gradualizer_cli.

This module contains entry points for calling into Gradualizer as a library, as well as some Erlang shell utilities for working directly with the type checker internals.

Let's try out the shell utilities by running:

  $ rebar3 shell

Now, we can play with some examples:

  > gradualizer:type_of("[ a || _ <- lists:seq(1, 5) ]").
  {type,0,list,[{atom,0,a}]}
  > typelib:pp_type(v(-1)).
  "[a]"
  > typechecker:normalize(gradualizer:type("a()"), gradualizer:env("-type a() :: integer().", [])).
  {type,0,integer,[]}
  > typelib:pp_type(v(-1)).
  "integer()"
  > gradualizer:type_of("fun (A) -> #{tag => my_map, list_of_as => [ A || _ <- lists:seq(1, 5) ]} end").
  {type,0,'fun',
        [{type,0,product,[{type,0,any,[]}]},
         {type,0,map,
               [{type,0,map_field_assoc,[{atom,0,tag},{atom,0,my_map}]},
                {type,0,map_field_assoc,
                      [{atom,0,list_of_as},{type,0,list,[{type,0,any,[]}]}]}]}]}
  > typelib:pp_type(v(-1)).
  "fun((any()) -> #{tag => my_map, list_of_as => [any()]})"
The main library API of Gradualizer are type_check_(file|module|dir) functions, which accept options described by the options() type.

Link to this section Summary

Types

The options accepted by the Gradualizer API.

Functions

See also: env/2.

See also: env/2.

Create a type checker environment populated by types defined in a source code snippet via -type ... and -record(...) attributes.

Return a Gradualizer type for the passed in Erlang type definition.

Type check all source or beam files in a directory.
Type check all source or beam files in a directory.
Type check a source or beam file
Type check a source or beam file
Type check a source or beam file
Type check a source or beam

Type check an abstract syntax tree of a module. This can be useful for tools where the abstract forms are generated in memory.

Type check a module
Type check a module

Infer type of an Erlang expression.

Link to this section Types

Specs

options() :: proplists:proplist().

The options accepted by the Gradualizer API.

  • {i, Dir}: include path for -include and -include_lib when checking Erlang source files. Specify multiple times for multiple include paths.
  • stop_on_first_error: if true stop type checking on the first error, if false continue checking all functions in the given file and all files in the given directory.
  • crash_on_error: if true crash on the first produced error.
  • return_errors: return a list of errors instead of printing them and returning a single condensed ok | nok.
  • fmt_location: how to format location when pretty printing errors:
    • none: no location for easier comparison
    • brief: for machine processing (<line>:<column>: before message text)
    • verbose (default): for human readers (on line <line> at column <column> within the message text)
  • fmt_expr_fun: function to pretty print an expression AST (useful to support other languages)
  • fmt_type_fun: function to pretty print a type AST (useful to support other languages)
  • {color, always | never | auto}: Use colors when printing fancy messages. Auto is the default but auto-detection of tty doesn't work when running as an escript. It works when running from the Erlang shell though.
  • {fancy, boolean()}: Use fancy error messages when possible. True by default. Doesn't work when a custom fmt_expr_fun is used.

Specs

top()

Link to this section Functions

Specs

env() -> typechecker:env().

See also: env/2.

Specs

See also: env/2.

Specs

env(string(), gradualizer:options()) -> typechecker:env().

Create a type checker environment populated by types defined in a source code snippet via -type ... and -record(...) attributes.

Currently, it's not possible to define variable bindings in the environment.

  > rr(typechecker).
  > gradualizer:env("-type a() :: integer().", []).
  #env{fenv = #{},imported = #{},venv = #{},
       tenv = #{module => undefined,records => #{},
                types => #{{a,0} => {[],{type,0,integer,[]}}}},
       infer = false,verbose = false,exhaust = true,
       clauses_stack = [],union_size_limit = 30,
       current_spec = none}
  > gradualizer:env("-record(r, {f}).", []).
  #env{
      fenv = #{},imported = #{},venv = #{},
      tenv =
          #{module => undefined,
            records =>
                #{r =>
                      [{typed_record_field,
                           {record_field,0,{atom,0,f},{atom,1,undefined}},
                           {type,0,any,[]}}]},
            types => #{}},
      infer = false,verbose = false,exhaust = true,
      clauses_stack = [],union_size_limit = 30,
      current_spec = none}

Specs

type(string()) -> typechecker:type().

Return a Gradualizer type for the passed in Erlang type definition.

  > gradualizer:type("[integer()]").
  {type,0,list,[{type,0,integer,[]}]}

Specs

type_check_dir(file:filename()) -> ok | nok | [{file:filename(), any()}].
Type check all source or beam files in a directory.
Link to this function

type_check_dir(Dir, Opts)

View Source

Specs

type_check_dir(file:filename(), options()) -> ok | nok | [{file:filename(), any()}].
Type check all source or beam files in a directory.

Specs

type_check_file(file:filename()) -> ok | nok | [{file:filename(), any()}].
Type check a source or beam file
Link to this function

type_check_file(File, Opts)

View Source

Specs

type_check_file(file:filename(), options()) -> ok | nok | [{file:filename(), any()}].
Type check a source or beam file

Specs

type_check_files([file:filename()]) -> ok | nok | [{file:filename(), any()}].
Type check a source or beam file
Link to this function

type_check_files(Files, Opts)

View Source

Specs

type_check_files([file:filename()], options()) -> ok | nok | [{file:filename(), any()}].
Type check a source or beam
Link to this function

type_check_forms(Forms, Opts)

View Source

Specs

type_check_forms([erl_parse:abstract_form()], options()) -> ok | nok | [{file:filename(), any()}].

Type check an abstract syntax tree of a module. This can be useful for tools where the abstract forms are generated in memory.

If the first form is a file attribute (as in forms returned by e.g. epp:parse_file/1,2), that filename will be used in error messages. The second form is typically the module attribute.
Link to this function

type_check_module(Module)

View Source

Specs

type_check_module(module()) -> ok | nok | [{file:filename(), any()}].
Type check a module
Link to this function

type_check_module(Module, Opts)

View Source

Specs

type_check_module(module(), options()) -> ok | nok | [{file:filename(), any()}].
Type check a module

Specs

type_of(string()) -> typechecker:type().

See also: type_of/2.

Specs

type_of(string(), typechecker:env()) -> typechecker:type().

Infer type of an Erlang expression.

  > gradualizer:type_of("[ a || _ <- lists:seq(1, 5) ]").
  {type,0,list,[{atom,0,a}]}
  > gradualizer:type_of("case 5 of 1 -> one; _ -> more end").
  {type,0,union,[{atom,0,more},{atom,0,one}]}
  > Env = gradualizer:env([{union_size_limit, 1}]).
  > gradualizer:type_of("case 5 of 1 -> one; _ -> more end", Env).
  {type,0,any,[]}