Specs for Functions and Callbacks

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
Line 6

values takes a collection (tuple or list) and returns a list of values (any).

Line 7

size takes a collection and returns an integer (>= 0).

Line 8

has_key? takes a collection and a key, and returns true or false.

Line 9

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]