Feel.Runtime.DecisionTable (feel v0.1.0)

Copy Markdown View Source

The run of a decision table (DMN 1.6, clause 8 and 10.3.2.10), for both backends.

A backend gives the table its parts as functions, and this module does what the specification says of them:

  1. The input expressions have been evaluated, in order, and inputs are their values, each with the function that tests it against the inputValues of its column, if it has some.
  2. A rule matches when every input satisfies the input entry of its column. An entry is a function of the input value and the environment that gives true or false (the unary tests of an entry are satisfied or not, never null, and not(…) is satisfied by an input that none of its tests matches). No entry is -, and satisfied by any input; if the column has input values it is satisfied by an input that satisfies them. A rule stops at the first entry that is not satisfied.
  3. The hit policy chooses among the rules that match, and the outputs of those are computed, and only those: an output entry of a rule that does not count is not run.
  4. No rule counts: the default output, or null.
PolicyResult
:uniquethe output of the one rule; more than one is null with the error :unique_hit_policy_violated
:firstthe output of the first rule
:anythe output of the first rule, if the outputs of all of them are equal (=), else null with :any_hit_policy_violated
:prioritythe output that comes first in the output values; one that is not among them is null with :output_not_in_values
:output_orderthe outputs in the order of the output values
:rule_orderthe list of the outputs, in the order of the rules
:collectthe list of the outputs, or sum, count, min or max of it with an aggregation

The output of a rule is its output entry, or, for a table of several outputs, a context of them by the names of the outputs. The order of the priority policies is by the output values of the first output, then of the second, and so on; an output that has none is not part of it.

Every error here is null for the table, reported at the span of the node, as the errors of the operators are (and ends a mode: :strict run).

Summary

Types

A function of an input and the environment that tells whether the input satisfies the entry of a rule.

An input: its value, and the test against the input values of its column, if it has one.

An output: the output values (the order of the priority policies) and the default output entry, if it has them.

A rule: its id, one entry for each input (nil is -), and one output entry for each output.

A function of the environment that computes a part of the table.

Types

entry()

@type entry() :: (Feel.Value.t(), Feel.Runtime.Env.t() ->
              {boolean(), Feel.Runtime.Env.t()})

A function of an input and the environment that tells whether the input satisfies the entry of a rule.

input()

@type input() :: {Feel.Value.t(), entry() | nil}

An input: its value, and the test against the input values of its column, if it has one.

options()

@type options() :: %{
  policy:
    :unique | :first | :any | :priority | :output_order | :rule_order | :collect,
  aggregation: :sum | :count | :min | :max | nil,
  names: [String.t() | nil],
  span: Feel.Diagnostic.span()
}

output()

@type output() :: {thunk() | nil, thunk() | nil}

An output: the output values (the order of the priority policies) and the default output entry, if it has them.

rule()

@type rule() :: {String.t(), [entry() | nil], [thunk()]}

A rule: its id, one entry for each input (nil is -), and one output entry for each output.

thunk()

@type thunk() :: (Feel.Runtime.Env.t() -> {Feel.Value.t(), Feel.Runtime.Env.t()})

A function of the environment that computes a part of the table.

Functions

run(env, options, inputs, rules, outputs)

@spec run(Feel.Runtime.Env.t(), options(), [input()], [rule()], [output()]) ::
  {Feel.Value.t(), Feel.Runtime.Env.t()}

Runs a table.

iex> env = Feel.Runtime.Env.new()
iex> equals = fn expected -> fn input, env -> {input == expected, env} end end
iex> rules = [{"r1", [equals.("a")], [fn env -> {1, env} end]}, {"r2", [equals.("b")], [fn env -> {2, env} end]}]
iex> options = %{policy: :unique, aggregation: nil, names: [nil], span: {0, 1}}
iex> {value, _env} = Feel.Runtime.DecisionTable.run(env, options, [{"b", nil}], rules, [{nil, nil}])
iex> value
2