The type of the result of a call of a built-in function, exact to the values of its arguments (D-161, step C).
Feel.Analysis.Calls.result_type/3 gives a call the types that the signatures of the function say (the kinds of the result
and Null). That is true and not exact: not(b) for a b that is true is false, string length(s) for a s in
{"a", "b"} is 1, abs(n) for an n in [-5..3] is [0..5], and none of them is null unless an argument is. A
family of rules (a module under Feel.Analysis.Images) says that, for the functions it names, from the types of the
arguments:
@behaviour Feel.Analysis.Images
def names, do: ["not"]
def image("not", [argument]), do: {:ok, ...}image/2 gives the whole type of the result, Null included, or :none for arguments the family has no rule for, and the
signatures answer. A rule is a claim about the run, so it is checked against it: Feel.ImageCheck (test/support) runs the
function on the values at the edges of the types of the arguments (Feel.Type.Witness.edges/2) and holds the result to the
rule. Every value the run gives is in the type, and a null that the type has is one that some arguments give: the first
is soundness (a type that is too small is a wrong answer, D-159), the second is that a null is in a type only if a value can
cause it (D-161).
The arguments are the types of the call as it is analyzed (Feel.IR.type/1: with the sets of values), in the order of the
parameters, and only the ones that are given (an optional parameter that is left out is not in the list). A rule is not asked
for a call that an extension may answer, whose function is not the one the rule is for.
What a rule does about null
The run gives null for a null argument, for an argument of a kind the function does not take (with an error), and for the
values of the right kinds that the function has no result for. with_null/2 adds Null for the first, and the rule says the
others: a null that is not in the type of the result has to be one that no value of the arguments causes. A function that
takes null as it takes any other value (is, string(null), get or else) says so in its rule.
Adding a family
A family is a module that has the behaviour, listed in @families below. It names the functions it has rules for, and gives
image/2 a clause for each, and a catch-all that is :none. Its test is a grid of argument types for each function, held
to the run with Feel.ImageCheck.check!/3.
Summary
Callbacks
The type of the result of a call, for the types of its arguments, or :none.
The names of the functions the family has rules for.
Functions
The families, in the order they are asked.
The type of the result of a call of the built-in function name for the types of its arguments, if a family has a rule for
it.
The type of a result with Null added when an argument can be null, which a call of a function that does not take null
gives null for.
Callbacks
@callback image(String.t(), [Feel.Type.Union.t()]) :: {:ok, Feel.Type.Union.t()} | :none
The type of the result of a call, for the types of its arguments, or :none.
@callback names() :: [String.t()]
The names of the functions the family has rules for.
Functions
@spec families() :: [module()]
The families, in the order they are asked.
@spec image(String.t(), [Feel.Type.Union.t()]) :: {:ok, Feel.Type.Union.t()} | :none
The type of the result of a call of the built-in function name for the types of its arguments, if a family has a rule for
it.
iex> Feel.Analysis.Images.image("not", [[{:boolean, true}]]) |> elem(1) |> Feel.Type.Union.format()
"boolean = false"
iex> Feel.Analysis.Images.image("no such function", [[:number]])
:none
iex> Feel.Analysis.Images.image("abs", [[{:number, Feel.Type.NumberSet.interval(Decimal.new(-5), Decimal.new(3))}]]) |> elem(1) |> Feel.Type.Union.format()
"number in [0..5]"
@spec with_null([Feel.Type.Union.t()], Feel.Type.Union.t()) :: Feel.Type.Union.t()
The type of a result with Null added when an argument can be null, which a call of a function that does not take null
gives null for.
iex> Feel.Analysis.Images.with_null([[:number], [:number, :null]], [:boolean])
[:boolean, :null]
iex> Feel.Analysis.Images.with_null([[:number]], [:boolean])
[:boolean]