# `Feel.Optimizer`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/optimizer.ex#L1)

The step between the analyzer and the backends: it removes from the IR what the analysis has
already settled (D-45).

`Feel.prepare/2` parses an expression, analyzes it, optimizes the IR that the analyzer
produced, and has a backend compile the result. The optimizer is a function from IR to IR,
and both backends run what it gives. It changes nothing a run can observe: the value, the
diagnostics and where they point, and the point at which a strict run ends are those of the IR
it was given. A test runs the same expressions both ways, on both backends, to check it.

## What it does

  * **Folds closed subtrees.** An operation whose operands are constants is evaluated, and
    the constant that comes out takes its place: `1 + 2` is `3`, `1 < 2` is `true`, and
    `1 + null` is `null`. A list or a context of constants is the list or the context, so
    `[1, 2 + 3]` is the list `[1, 5]`. So are a path or a descendant of a constant, a range, `between`,
    `instance of` and a filter whose brackets read nothing but its elements, and the call of a built-in
    function that depends on its arguments alone (`Feel.Builtin.Function`, `pure?`), on constants:
    `abs(-3)` is `3`, but `now()` reads the clock and stays. Subtrees are taken from the bottom, so
    `x + (1 + 2)` becomes `x + 3`.
    The evaluation is the runtime's own (`Feel.Backend.Interpreter`), with the limits of the
    expression. It is only used when the run **reports nothing**: `1 / 0` is `null` with an
    error, and a strict run ends there, so it stays an operation. So does a result that is
    past a limit (a string that is too long).
  * **Settles a value compared with itself** (D-170). `=` of two operands that are the same read and cannot report is
    `true`, and `!=` is `false`, for every value, `null` included (`Feel.Analysis.Identity`). The analyzer does it for
    the operands it sees to be the same; here it is done again after the operands are folded, which can make two
    operands the same: `[i, -1] = [i, 0 - 1]`, or `(if 1 < 2 then i else 0) = i`. A name of the bindings is not
    settled, since the read of it reports a name that is not bound (D-37).
  * **Takes the branch of a conditional whose condition is a constant.** `if true then a else b`
    is `a`, and so is `if false then b else a`, `null` and a value that is not a boolean being
    as `false`. The choice reports nothing and the other branch never runs, so the branch that
    is left is the conditional, with its own node and so its own spans. A branch is optimized
    only if it is the one that is left. A condition that is not a constant is left as it is,
    even when its type settles it: reading a name reports a name that is not bound.
  * **Drops a conversion that is not needed.** The `:conform` node of the declared type of the
    result (D-138) goes when the type of the expression is already within the declared one, as
    it is for `1 + 2` and `number`. A value of that type is not changed by the conversion.
  * **Drops the verification of the result's allowed values** (D-138) when the analysis has shown that
    every value the expression can have satisfies them, and that the tests report nothing
    while they do (an `if` among them reports nothing if its condition and the branches it can take
    do not). For a result that is `3` and the tests `[0..10]` that is so. It also goes
    when the result is `null` for sure, which is exempt, so the tests are never run.

## What it leaves

Optimizing a function body preserves its captured `meta.returns` (D-219). Narrowing or folding the
body cannot change runtime signature inspection or make a previously rejected callback acceptable.
Body call refinements can remain more precise than this observable signature. A result check stays
whenever the inferred possible values do not establish that the conversion or verification is redundant.

It does not look at what the types say for an expression with a name in it: the read of a name
reports a name that is not bound (D-37), and the order of evaluation is observable (D-53). A
result that certainly fails its allowed values keeps its verification, which reports the error; the
analyzer is where that is warned about. The verifications and conversions of the names at the start of
a run (D-138) stay. A call that is a certain error is left for the run to report, a call that reports
something is left too (the spelling of a text that an extension reads, an `:info` of every run, D-81), a call of
a function that is not pure is left (`uuid()`), and the call of a function value is never folded, since the callee
is only known when the run begins (D-82).

The fold is evaluated with the `:dialect` and the `:extensions` of the expression (D-45), as its run will be, so a
call of a function that an extension adds, or one that an extension gives a signature, is folded as a call of
DMN 1.6 is: `trim(" a ")` is `"a"` where `:camunda` is on.

Nothing in the IR changes in kind: a constant has the span of the operation it replaces and
the exact type of its value.

    iex> {:ok, ast} = Feel.parse("1 + 2 * 3")
    iex> {:ok, %{ir: ir}} = Feel.Analysis.Analyzer.analyze(ast, [])
    iex> Feel.IR.kind(ir)
    :arithmetic
    iex> {:const, _meta, value} = Feel.Optimizer.optimize(ir)
    iex> Decimal.equal?(value, 7)
    true

# `optimize_option`

```elixir
@type optimize_option() ::
  {:limits, Feel.Limits.t()}
  | {:dialect, Feel.Dialect.t()}
  | {:extensions, Feel.Extensions.t() | [atom()]}
```

# `optimize`

```elixir
@spec optimize(Feel.IR.t(), [optimize_option()]) :: Feel.IR.t()
```

Optimizes an IR. The options are the ones that a fold is evaluated with: `:limits`, a `t:Feel.Limits.t/0` that
a fold is evaluated within (a result past a limit is not folded), and the `:dialect` and the `:extensions` of the
expression (D-81), so that a call of a function that an extension adds, or overloads, is the one the run will make.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
