for, some and every (DMN 1.6, section 10.3.2.14 and Table 50; D-86).
A backend does not loop. It gives this module the domains, the body, and where they are in the source,
and this module runs the loops, so both backends iterate in the same way, count the same iterations, and end
at the same limit. A domain and the body are functions of the environment, because they are code of the
expression: the interpreter's walk of a node, and a fun of the generated code.
- a domain is
fn env, earlier -> {raw, env} end, whereearlieris the list of the values of the iterators before it. A later domain reads the earlier iterators (for x in [[1, 2], [3, 4]], y in x, TCK 0084 #015), so it is evaluated again for each of their values.rawis a value, or{:through, from, to}forfrom..to, which has no value of its own. With the:readsoption (below)earlieris only the values the domain reads. - the body is
fn env, values, partial -> {value, env} end, with the value of each iterator, in order, and the results so far (partial;[]when the body does not read it). With the:partialoption (below) the results are given as the list, oldest first (true), or as the results a body indexes (:view).
What a domain is given
A domain is given the values of the iterators before it, all of them, as a list. That is a list of k values for
the domain of the kth iterator, and the backend that binds them by name does k things with it, so a loop of n
iterators costs n * n / 2 whatever its domains read (D-86). The :reads option of for_loop/4, some/4 and
every/4 is a list with one entry for each domain, :all or the places (0 for the first iterator) of the ones
the domain reads, and a domain with places is given the values at them, in that order, and no others. The
backends give places to the domains that have many iterators before them (more than 16) and leave :all for the
rest. Without the option every domain is given all of them, as it was before it, so code that was made without it
runs as it did.
What a body is given as partial
A body that reads partial as a value (sum(partial), partial as an argument, a path, a filter that is not an
index, a nested loop over it) is given the list of the results so far, oldest first: the loop keeps them newest first,
to put one in with a cons, and makes the list for each run of the body, which is m cells for the mth. That is
what the :partial option true says. A body that reads it only as partial[expr] or count(partial) says :view,
and is given the results as Feel.Runtime.Partial (D-86), that the loop puts each result in as it makes it and the
reads look in, so there is no list to make and a read does not depend on how many results there are
(Feel.Analysis.Iterations.view/2 is where that is decided, and the backends read it with Partial.at/4 and
Partial.count/3). A loop that is told true and does not read partial is as it was.
The loops
The first iterator is the outermost loop and the last varies fastest, so
for i in [1, 2], j in [3, 4] is (1, 3), (1, 4), (2, 3), (2, 4). for is the list of the bodies, in that order;
some is false or r1 or r2 or ... and every is true and r1 and r2 and ..., in the three-valued logic
(Feel.Runtime.Logic): a true decides a some and a false an every, whatever else is null. Every
iteration is evaluated, as every operand of an operator is (D-53), so what a body reports is reported. A
product with no element is [], false for some and true for every.
A strict quantifier
some and every of a boxed expression (strict: true, Feel.Runtime.Boxed, D-212) are null with an error, for the whole
expression, when the body is a value that is neither a boolean nor null: the loop ends there. Without the option a body that is
not a boolean is null for the fold, as the three-valued logic has it.
The domains
| The domain | The elements |
|---|---|
| a list | its elements, in order |
from..to of whole numbers | from, from + 1, ..., to, or down by 1 when to is below |
from..to of dates | each day from from to to, or down |
any other value, not null | the value alone, as a list of one (section 10.3.2.9.4), as an info |
Both ends of from..to are included. These are null, with an error, for the whole expression, and
nothing runs: null as a domain; a range value, [1..2], which is not 1..2 (TCK 0084 #023 to #025); and
from..to of anything but two whole numbers or two dates ("a".."z", 1.5..4, times, date and times,
durations, null, a number and a date).
Limits
Every element that an iterator takes counts as an iteration, and the run ends, in either mode, with an error of
kind :limit when they pass max_iterations (Feel.Runtime.Env.count_iteration/1). A range is walked, and
not made, so for i in 1..1000000000 return i stops at the limit and not at the memory.
The list that a for makes is counted as it grows, and the run ends in the same way at the first result over
max_list_length (Feel.Runtime.Env.check_list_length/2), where it ended when the finished list was refused. The error
is the same, and carries the diagnostics of the run until that result and not those of the iterations that were left
(D-154). A for inside another has a list of its own, and some and every make none.
Summary
Types
What the loop is told besides the code: the span of the expression, the span of each domain (for the
diagnostics about it), whether the body reads partial and how (:partial: false, true for the list of the
results, :view for Feel.Runtime.Partial), and what each domain reads (:reads, above).
What a domain gives: a value, or the two ends of from..to.
Functions
every: true and r1 and r2 ... for the results of the body.
for: the list of the values of the body for each iteration, or null with the error of a domain that cannot be
iterated.
some: false or r1 or r2 ... for the results of the body.
Types
@type body() :: (Feel.Runtime.Env.t(), [Feel.Value.t()], [Feel.Value.t()] -> {Feel.Value.t(), Feel.Runtime.Env.t()})
@type domain() :: (Feel.Runtime.Env.t(), [Feel.Value.t()] -> {raw(), Feel.Runtime.Env.t()})
@type options() :: %{ :span => Feel.Diagnostic.span(), :spans => [Feel.Diagnostic.span()], optional(:partial) => boolean() | :view, optional(:strict) => boolean(), optional(:reads) => [:all | [non_neg_integer()]] }
What the loop is told besides the code: the span of the expression, the span of each domain (for the
diagnostics about it), whether the body reads partial and how (:partial: false, true for the list of the
results, :view for Feel.Runtime.Partial), and what each domain reads (:reads, above).
@type raw() :: Feel.Value.t() | {:through, Feel.Value.t(), Feel.Value.t()}
What a domain gives: a value, or the two ends of from..to.
Functions
@spec every(Feel.Runtime.Env.t(), [domain()], body(), options()) :: {Feel.Value.t(), Feel.Runtime.Env.t()}
every: true and r1 and r2 ... for the results of the body.
iex> env = Feel.Runtime.Env.new()
iex> options = %{span: {0, 5}, spans: [{0, 1}]}
iex> body = fn env, [i], _partial -> {i != nil and i > 0, env} end
iex> {false, _env} = Feel.Runtime.Iteration.every(env, [fn env, [] -> {[1, nil], env} end], body, options)
iex> {true, _env} = Feel.Runtime.Iteration.every(env, [fn env, [] -> {[], env} end], body, options)
@spec for_loop(Feel.Runtime.Env.t(), [domain()], body(), options()) :: {Feel.Value.t(), Feel.Runtime.Env.t()}
for: the list of the values of the body for each iteration, or null with the error of a domain that cannot be
iterated.
iex> env = Feel.Runtime.Env.new()
iex> domains = [fn env, [] -> {[Decimal.new(1), Decimal.new(2)], env} end]
iex> body = fn env, [i], _partial -> {Decimal.add(i, 10), env} end
iex> {values, _env} = Feel.Runtime.Iteration.for_loop(env, domains, body, %{span: {0, 5}, spans: [{0, 1}]})
iex> Enum.map(values, &Decimal.to_integer/1)
[11, 12]
@spec some(Feel.Runtime.Env.t(), [domain()], body(), options()) :: {Feel.Value.t(), Feel.Runtime.Env.t()}
some: false or r1 or r2 ... for the results of the body.
iex> env = Feel.Runtime.Env.new()
iex> options = %{span: {0, 5}, spans: [{0, 1}]}
iex> body = fn env, [i], _partial -> {i == 2, env} end
iex> {true, _env} = Feel.Runtime.Iteration.some(env, [fn env, [] -> {[1, 2, 3], env} end], body, options)
iex> {false, _env} = Feel.Runtime.Iteration.some(env, [fn env, [] -> {[], env} end], body, options)