The @spec specifies a function’s parameter count, types, and return-value type. It can appear anywhere in a module that defines the function, but by convention it sits immediately before the function definition, following any function documentation.
We’ve already seen the syntax:
@spec function_name( param1_type, …) :: return_type
Let’s see some examples. These come from the built-in Dict module.
1: | @type key :: any |
2: | @type value :: any |
3: | @type keys :: [ key ] |
4: | @type t :: tuple | list # `t` is the type of the collection |
5: | |
6: | @spec values(t) :: [value] |
7: | @spec size(t) :: non_neg_integer |
8: | @spec has_key?(t, key) :: boolean |
9: | @spec update(t, key, value, (value -> value)) :: t |
values takes a collection (tuple or list) and returns a list of values (any).
size takes a collection and returns an integer (>= 0).
has_key? takes a collection and a key, and returns true or false.
update takes a collection, a key, a value, and a function that maps a value to a value. It returns a (new) collection.
For functions with multiple heads (or those that have default values), you can specify multiple @spec attributes. Here’s an example from the Enum module:
| @spec at(t, index) :: element | nil |
| @spec at(t, index, default) :: element | default |
| |
| def at(collection, n, default \\ nil) when n >= 0 do |
| ... |
| end |
The Enum module also has many examples of the use of as_boolean:
| @spec filter(t, (element -> as_boolean(term))) :: list |
| def filter(collection, fun) when is_list(collection) do |
| ... |
| end |
This says filter takes something enumerable and a function. That function maps an element to a term (which is an alias for any), and the filter function treats that value as being truthy. filter returns a list.
For more information on Elixir support for type specifications, see the guide.[42]