Type.Function (mavis v0.0.2) View Source
Represents a function type.
There are two fields for the struct defined by this module.
params
a list of types for the function arguments. Note that the arity of the function is the length of this list. May also be the atom:any
which corresponds to "a function of any arity".return
the type of the returned value.
Examples:
(... -> integer())
would be represented as%Type.Function{params: :any, return: %Type{name: :integer}}
(integer() -> integer())
would be represented as%Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}}
Inference
By default, Mavis will not attempt to perform inference on function types.
iex> inspect Type.of(&(&1 + 1))
"(any() -> any())"
If you would like to perform inference on the function to obtain
more details on the acceptable function types, set the inference
environment variable. For example, if you're using the :mavis_inference
hex package, do:
Application.put_env(:mavis, :inference, Type.Inference)
The default module for this is Type.NoInference
Key functions:
comparison
Functions are ordered first by the type order on their return type, followed by type order on their parameters.
iex> Type.compare(%Type.Function{params: [], return: %Type{name: :atom}},
...> %Type.Function{params: [], return: %Type{name: :integer}})
:gt
iex> Type.compare(%Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}},
...> %Type.Function{params: [%Type{name: :atom}], return: %Type{name: :integer}})
:lt
intersection
Functions with distinct parameter types are nonoverlapping, even if their parameter types overlap. If they have the same parameters, then their return values are intersected.
iex> Type.intersection(%Type.Function{params: [], return: 1..10},
...> %Type.Function{params: [], return: %Type{name: :integer}})
%Type.Function{params: [], return: 1..10}
iex> Type.intersection(%Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}},
...> %Type.Function{params: [1..10], return: %Type{name: :integer}})
%Type{name: :none}
functions with :any
parameters intersected with a function with specified parameters
will adopt the parameters of the intersected function.
iex> Type.intersection(%Type.Function{params: :any, return: %Type{name: :integer}},
...> %Type.Function{params: [1..10], return: %Type{name: :integer}})
%Type.Function{params: [1..10], return: %Type{name: :integer}}
union
Functions are generally not merged in union operations, but if their parameters are identical then their return types will be merged.
iex> Type.union(%Type.Function{params: [], return: 1..10},
...> %Type.Function{params: [], return: 11..20})
%Type.Function{params: [], return: 1..20}
subtype?
A function type is the subtype of another if it has the same parameters and its return value type is the subtype of the other's
iex> Type.subtype?(%Type.Function{params: [%Type{name: :integer}], return: 1..10},
...> %Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}})
true
usable_as
The usable_as
relationship for functions may not necessarily be obvious. An
easy way to think about it, is: if I passed a function with this type to a
function that demanded the other type how confident would I be that it would
not crash.
A function is usable_as
another function if all of its parameters are
supertypes of the targeted function; and if its return type is subtypes of the
return type of the targeted function.
iex> Type.usable_as(%Type.Function{params: [%Type{name: :integer}], return: 1..10},
...> %Type.Function{params: [1..10], return: %Type{name: :integer}})
:ok
iex> Type.usable_as(%Type.Function{params: [1..10], return: 1..10},
...> %Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}})
{:maybe, [%Type.Message{type: %Type.Function{params: [1..10], return: 1..10},
target: %Type.Function{params: [%Type{name: :integer}], return: %Type{name: :integer}}}]}
iex> Type.usable_as(%Type.Function{params: [], return: %Type{name: :atom}},
...> %Type.Function{params: [], return: %Type{name: :integer}})
{:error, %Type.Message{type: %Type.Function{params: [], return: %Type{name: :atom}},
target: %Type.Function{params: [], return: %Type{name: :integer}}}}