## 7.2 Types and their Syntax

Types describe sets of Erlang terms. Types consist of, and are built from, a set of predefined types, for example, integer(), atom(), and pid(). Predefined types represent a typically infinite set of Erlang terms that belong to this type. For example, the type atom() denotes the set of all Erlang atoms.

For integers and atoms, it is allowed for singleton types; for example, the integers -1 and 42, or the atoms 'foo' and 'bar'. All other types are built using unions of either predefined types or singleton types. In a type union between a type and one of its subtypes, the subtype is absorbed by the supertype. Thus, the union is then treated as if the subtype was not a constituent of the union. For example, the type union:

  atom() | 'bar' | integer() | 42

describes the same set of terms as the type union:

  atom() | integer()

Because of subtype relations that exist between types, types form a lattice where the top-most element, any(), denotes the set of all Erlang terms and the bottom-most element, none(), denotes the empty set of terms.

The set of predefined types and the syntax for types follows:

  Type :: any()                 %% The top type, the set of all Erlang terms        | none()                %% The bottom type, contains no terms        | pid()        | port()        | reference()        | []                    %% nil        | Atom        | Bitstring        | float()        | Fun        | Integer        | List        | Map        | Tuple        | Union        | UserDefined           %% described in Type Declarations of User-Defined Types   Atom :: atom()        | Erlang_Atom           %% 'foo', 'bar', ...   Bitstring :: <<>>             | <<_:M>>          %% M is an Integer_Value that evaluates to a positive integer             | <<_:_*N>>        %% N is an Integer_Value that evaluates to a positive integer             | <<_:M, _:_*N>>   Fun :: fun()                  %% any function       | fun((...) -> Type)     %% any arity, returning Type       | fun(() -> Type)       | fun((TList) -> Type)   Integer :: integer()           | Integer_Value           | Integer_Value..Integer_Value      %% specifies an integer range   Integer_Value :: Erlang_Integer              %% ..., -1, 0, 1, ... 42 ...                 | Erlang_Character            %% $a,$b ...                 | Integer_Value BinaryOp Integer_Value                 | UnaryOp Integer_Value   BinaryOp :: '*' | 'div' | 'rem' | 'band' | '+' | '-' | 'bor' | 'bxor' | 'bsl' | 'bsr'   UnaryOp :: '+' | '-' | 'bnot'   List :: list(Type)                           %% Proper list ([]-terminated)        | maybe_improper_list(Type1, Type2)    %% Type1=contents, Type2=termination        | nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above        | nonempty_list(Type)                  %% Proper non-empty list   Map :: #{}                                   %% denotes the empty map       | #{AssociationList}   Tuple :: tuple()                             %% denotes a tuple of any size         | {}         | {TList}   AssociationList :: Association                   | Association, AssociationList   Association :: Type := Type                  %% denotes a mandatory association               | Type => Type                  %% denotes an optional association   TList :: Type         | Type, TList   Union :: Type1 | Type2

Integer values are either integer or character literals or expressions consisting of possibily nested unary or binary operations that evaluate to an integer. Such expressions can also be used in bit strings and ranges.

The general form of bit strings is <<:M, :N>>, where M and N must evaluate to positive integers. It denotes a bit string that is M + (kN) bits long (that is, a bit string that starts with M bits and continues with k segments of N bits each, where k is also a positive integer). The notations <<:*N>>, <<:M>>, and <<>> are convenient shorthands for the cases that M or N, or both, are zero.

Because lists are commonly used, they have shorthand type notations. The types list(T) and nonempty_list(T) have the shorthands [T] and [T,…], respectively. The only difference between the two shorthands is that [T] can be an empty list but [T,…] cannot.

Notice that the shorthand for list(), that is, the list of elements of unknown type, is [_] (or [any()]), not []. The notation [] specifies the singleton type for the empty list.

The general form of map types is #{AssociationList}. The key types in AssociationList are allowed to overlap, and if they do, the leftmost association takes precedence. A map association has a key in AssociationList if it belongs to this type. AssociationList can contain both mandatory (:=) and optional (=>) association types. If an association type is mandatory, an association with that type needs to be present. In the case of an optional association type it is not required for the key type to be present.

The notation #{} specifies the singleton type for the empty map. Note that this notation is not a shorthand for the map() type.

For convenience, the following types are also built-in. They can be thought as predefined aliases for the type unions also shown in the table.

 Built-in type Defined as term() any() binary() <<:8>> bitstring() <<:1>> boolean() 'false' | 'true' byte() 0..255 char() 0..16#10ffff nil() [] number() integer() | float() list() [any()] maybe_improper_list() maybe_improper_list(any(), any()) nonempty_list() nonempty_list(any()) string() [char()] nonempty_string() [char(),…] iodata() iolist() | binary() iolist() maybe_improper_list(byte() | binary() | iolist(), binary() | []) map() #{any() => any()} function() fun() module() atom() mfa() {module(),atom(),arity()} arity() 0..255 identifier() pid() | port() | reference() node() atom() timeout() 'infinity' | non_neg_integer() no_return() none()

Table 7.1: Built-in types, predefined aliases

In addition, the following three built-in types exist and can be thought as defined below, though strictly their "type definition" is not valid syntax according to the type language defined above.

 Built-in type Can be thought defined by the syntax non_neg_integer() 0.. pos_integer() 1.. neg_integer() ..-1

Users are not allowed to define types with the same names as the predefined or built-in ones. This is checked by the compiler and its violation results in a compilation error.

Note

The following built-in list types also exist, but they are expected to be rarely used. Hence, they have long names:

  nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any(), any())  nonempty_improper_list(Type1, Type2)  nonempty_maybe_improper_list(Type1, Type2)

where the last two types define the set of Erlang terms one would expect.

Also for convenience, record notation is allowed to be used. Records are shorthands for the corresponding tuples:

  Record :: #Erlang_Atom{}          | #Erlang_Atom{Fields}

Records are extended to possibly contain type information. This is described in Type Information in Record Declarations.