Deviations: Functions

Top-arity Functions

Rationale

Top-arity functions represent the "top type" for function of a given arity. In the Mavis typesystem, the parameters to the function signature signify "which types may be passed and are guaranteed to not crash the function. Accordingly, the type-signature any() -> any() represents a smaller population of functions than the type-signature integer() -> any(), as the former type signature represents that all values are guaranteed to succeed, whereas the later represents that only integers are guaranteed to succeed, a less stringent criterion.

Erlang does have a "top type" for functions, namely the any function, which is denoted in Elixir as... -> any(). However, it is important to note that there are cases when it is reasonable to be able to select on an arity-specific top type. See the example below.

Nomenclature

in Mavis, this type is represented as _, _ -> result_type(), where the number of underscores corresponds to the arity of this function type.

internally, these functions are represented by placing a positive integer in the :params field of the Type.Function.t struct.

Example

def my_function(f) when is_function(f, 1) do
  :some_result
end

should have signature (_ -> any()) :: :some_result.

note that Dialyzer gives this function the signature (any() -> any()) :: :some_result, which suggests that a lambda passed to my_function must be able to tolerate any input. In reality, this function will not crash if passed, for example :erlang.byte_size/1 which has a more restrictive signature.

A truly any() -> any() lambda would be for example IO.inspect/1.

Notes

At this point, it doesn't seem to qualify the any() -> any() as a fully qualified "subtype" of the integer() -> any(), though that may be implemented later.

There is no zero-arity top-arity function.

Normalization to erlang function types

the :params field is replaced by a list of any() types of the appropriate arity. Namely the following happens:

_ -> any() to any() -> any() _, _ -> any() to any(), any() -> any()

when performing type inference, any function which is specced to take lambdas with all any() parameters should be viewed with suspicion and subjected to inference.