The types of for, some and every (D-86), which the analyzer reads off the types of the domains and of the body.
What the iterator is bound to is the element of the domain's type, as Feel.Runtime.Iteration takes it:
- a list gives the type of its elements, so
for i in [1, 2, 3]has anithat isnumber in 1, 2, 3, and a list of the declared typelist<number>givesnumber | Null; from..togives anumberwhen both ends can be numbers and adatewhen both can be dates;- a value that is not a list, a range or
nullis the element itself (a list of one); nulland a range ([1..2], which is not1..2) have no element: the expression isnullwith an error;Anyis a list of anything or a value: its element isAny, and it can fail.
Each domain also says whether it can fail, which makes the whole expression null, so the type of a for is a
list of the type of its body and Null where a domain can fail, and whether it fails always, which the analyzer
warns about.
A some is true if its body can be true for an element, false if it can be false for all of them or if there
is no element, and null if the body can be null or something that is not a boolean, or a domain can fail; an every
is the same with true and false changing places (Table 50).
Summary
Types
What the type of a domain gives: the elements, whether it can fail, and whether it always does.
Functions
The elements of a domain that is an expression, from its type.
The type of a for: a list of the type of the body, and Null where a domain can fail.
Whether a domain of this type has an element for sure: it is a value that is not a list, a range or null, and so a list of one.
The type of a some or an every over a domain of which every element is tested as its own (D-170): bodies is the type of
the test for each element, and there is one at least. The run folds the results from false (some) or true (every) with
the three-valued or or and (Table 50), so some is true if a test can be, false if all can be, and null if no test
is true and some is neither true nor false; every is the other way round. A some over 1, 2, 3 with i > 2 is
true for the test of 3, whatever i > 2 is for the others.
The type of a some (kind :some) or an every, from the type of the body.
The elements of from..to, from the types of its ends: whole numbers when both can be numbers, and dates when both
can be dates. It fails where an end can be null or of another kind, or a number that is not whole; an end that is
a single whole number (1..10) is all that a number is known to be whole by.
Whether the body of a for reads partial only by index and count (D-86), and the body with those reads as nodes
of their own. id is the id of the frame that binds partial for the loop (meta.id of the :local nodes that read
it), which tells its reads from those of a partial that something inside the body binds again.
Types
@type domain() :: %{ elements: Feel.Type.Union.t(), fail?: boolean(), always_fails?: boolean() }
What the type of a domain gives: the elements, whether it can fail, and whether it always does.
Functions
@spec elements(Feel.Type.Union.t()) :: domain()
The elements of a domain that is an expression, from its type.
iex> Feel.Analysis.Iterations.elements([{:list, [:number]}])
%{elements: [:number], fail?: false, always_fails?: false}
iex> Feel.Analysis.Iterations.elements([{:list, [:number]}, :null])
%{elements: [:number], fail?: true, always_fails?: false}
iex> Feel.Analysis.Iterations.elements([:string])
%{elements: [:string], fail?: false, always_fails?: false}
iex> Feel.Analysis.Iterations.elements([:null])
%{elements: [:null], fail?: true, always_fails?: true}
@spec for_type(Feel.Type.Union.t(), boolean()) :: Feel.Type.Union.t()
The type of a for: a list of the type of the body, and Null where a domain can fail.
@spec nonempty?(Feel.Type.Union.t()) :: boolean()
Whether a domain of this type has an element for sure: it is a value that is not a list, a range or null, and so a list of one.
@spec quantifier_over(:some | :every, [Feel.Type.Union.t(), ...]) :: Feel.Type.Union.t()
The type of a some or an every over a domain of which every element is tested as its own (D-170): bodies is the type of
the test for each element, and there is one at least. The run folds the results from false (some) or true (every) with
the three-valued or or and (Table 50), so some is true if a test can be, false if all can be, and null if no test
is true and some is neither true nor false; every is the other way round. A some over 1, 2, 3 with i > 2 is
true for the test of 3, whatever i > 2 is for the others.
iex> Feel.Analysis.Iterations.quantifier_over(:some, [[{:boolean, false}], [{:boolean, false}], [{:boolean, true}]])
[{:boolean, true}]
iex> Feel.Analysis.Iterations.quantifier_over(:some, [[{:boolean, false}], [{:boolean, false}]])
[{:boolean, false}]
iex> Feel.Analysis.Iterations.quantifier_over(:some, [[{:boolean, false}], [:boolean, :null]])
[:boolean, :null]
iex> Feel.Analysis.Iterations.quantifier_over(:every, [[{:boolean, true}], [{:boolean, false}]])
[{:boolean, false}]
iex> Feel.Analysis.Iterations.quantifier_over(:every, [[{:boolean, true}], [{:boolean, true}, :null]])
[{:boolean, true}, :null]
@spec quantifier_type( :some | :every, Feel.Type.Union.t(), :always | :never | :maybe, boolean() ) :: Feel.Type.Union.t()
The type of a some (kind :some) or an every, from the type of the body.
emptiness says whether the product of the domains is empty: :always, :never, or :maybe. fail? says that a
domain can fail.
iex> Feel.Analysis.Iterations.quantifier_type(:some, [{:boolean, true}], :never, false)
[{:boolean, true}]
iex> Feel.Analysis.Iterations.quantifier_type(:some, [{:boolean, true}], :maybe, false)
[:boolean]
iex> Feel.Analysis.Iterations.quantifier_type(:some, [{:boolean, true}], :always, false)
[{:boolean, false}]
iex> Feel.Analysis.Iterations.quantifier_type(:every, [:boolean, :null], :never, false)
[:boolean, :null]
iex> Feel.Analysis.Iterations.quantifier_type(:every, [{:boolean, true}], :never, false)
[{:boolean, true}]
@spec through(Feel.Type.Union.t(), Feel.Type.Union.t()) :: domain()
The elements of from..to, from the types of its ends: whole numbers when both can be numbers, and dates when both
can be dates. It fails where an end can be null or of another kind, or a number that is not whole; an end that is
a single whole number (1..10) is all that a number is known to be whole by.
iex> Feel.Analysis.Iterations.through([:number, :null], [:number])
%{elements: [:number], fail?: true, always_fails?: false}
iex> Feel.Analysis.Iterations.through([:date], [:date])
%{elements: [:date], fail?: false, always_fails?: false}
iex> Feel.Analysis.Iterations.through([:string], [:string])
%{elements: [:null], fail?: true, always_fails?: true}
@spec view(Feel.IR.t(), pos_integer()) :: {:view, Feel.IR.t()} | :list
Whether the body of a for reads partial only by index and count (D-86), and the body with those reads as nodes
of their own. id is the id of the frame that binds partial for the loop (meta.id of the :local nodes that read
it), which tells its reads from those of a partial that something inside the body binds again.
A read is partial[index], a filter whose brackets can only be a number (:index, which Feel.Analysis.Filters.mode/2
decides), and count(partial) as a call by position of the function of DMN 1.6: the brackets of another mode can be a
predicate or whatever the value says, and a call by name or one that an extension may answer is checked or answered as
the function sees fit. They become {:partial_at, meta, index} and {:partial_count, meta}, which the backends run
with Feel.Runtime.Partial and not with the list of the results. A body with any other read of partial (an argument,
partial alone, a path, the brackets of a predicate, a name that a filter may read as an entry of its elements, a loop
over it) is :list and is not changed: it is given the list, as it always was.
iex> {:ok, ast} = Feel.parse("for i in 1..3 return if i < 3 then 1 else partial[i - 1] + count(partial)")
iex> {:ok, %{ir: {:for, %{partial: partial}, _iterators, _body}}} = Feel.Analysis.Analyzer.analyze(ast, [])
iex> partial
:view
iex> {:ok, ast} = Feel.parse("for i in 1..3 return sum(partial)")
iex> {:ok, %{ir: {:for, %{partial: partial}, _iterators, _body}}} = Feel.Analysis.Analyzer.analyze(ast, [])
iex> partial
true