Compiles a Dmn.Model to a Dmn.Compiled: a prepared FEEL expression for each element of the decision requirements graph, and the
plan that says what each reads.
Everything that does not depend on the data is done here, once:
- The models. The models that the first one imports are read through the resolver of the caller (
Dmn.Imports,Dmn.Resolver); their elements are elements of the same graph. An element of an imported model is calledImport.Nameby the model that imports it, and its types areImport.tName. - The types. The item definitions of a model, and those of the models it imports under the names of the imports, become a
Feel.ItemDefinitions(Dmn.Compiler.Types). The declared type of a variable is FEEL's type, with its allowed values and its type constraints. By default recursive item references are errors. Withfeatures: [:recursion], guarded data cycles retain finite graphs: every cycle must cross a context member or list element, and none may cross a function signature. Runtime validation checks complete finite values; analysis uses conservative upper bounds (DD-22). - The graph. The elements are indexed by their
id(and the namespace of their model, for those of an import), the requirements are resolved to the elements they name, and a cycle is an error (DMN 1.6, 6.3: the requirements are acyclic): the elements on it have no logic. Unless the caller asks forrecursion: true(DD-22): then business knowledge models and decision services that read themselves or each other are recursion groups, which are compiled as any unit is and run with links to one another (Feel.Recursion); a cycle that has a decision on it is still an error. - The units. Each decision, business knowledge model and decision service has a scope (
Feel.Scope): the input data and decisions it requires, each of its declared type and allowed values, and the functions it invokes, each as the function type it has. Its boxed expression is mapped to the syntax tree of FEEL (Dmn.Compiler.Expression, andDmn.Compiler.Tablefor a decision table), andFeel.prepare_ast/3analyzes it, types it with that scope, and compiles it for the backend (the interpreter unlessbackend:says otherwise). The declared type of a decision's variable, and its allowed values, are how its result is converted and verified.
A decision is never run here, and nothing is evaluated: a text that cannot be read, a name that is not in scope and a table
with an entry that cannot be satisfied are diagnostics of the element (Dmn.Diagnostic), and the model is compiled all the same,
as DMN has an expression that cannot be evaluated give null (7.3.8). Dmn.Compiled.diagnostics has all of them.
Summary
Functions
Compiles a model.
Functions
@spec compile(Dmn.Model.t(), keyword()) :: {:ok, Dmn.Compiled.t()} | {:error, Dmn.Error.t()}
Compiles a model.
Options:
:resolver: how the models that it imports are found (Dmn.Resolver). None by default, and then an import is a diagnostic.:limits: theDmn.Limitsthat the imported models are read within.:feel_limits: theFeel.Limitsthat the expressions are prepared and run within (Feel.Limits.default/0by default). The compiled model keeps them (Dmn.Compiled.feel_limits), and a run keeps to them unless it is given:limitsof its own, which replace them (Dmn.Run).:check_tables: whether the decision tables are asked what their types say of them (Dmn.Compiler.Checks);trueby default.:backend: what the prepared expressions run on (Feel.Backend)::interpreter(the default), which generates no code and loads nothing into the VM, or:beam, which compiles each expression to a module and is faster. The BEAM backend loads modules and mints atoms that are never freed, so it is for models that are trusted (DD-41). Both give the same results. Withrecursion: truethe business knowledge models that have logic, and the decision services, stay on the interpreter, whatever is asked, since they can invoke themselves.:features: what the caller switches on (Dmn.Features, DD-29)::recursion, and the extensions (Dmn.Extension) by name or module. None by default. The two options below are the spellings that came first, and add to it.:recursion: whether a business knowledge model or a decision service may invoke itself, and may be invoked by what it invokes (DD-22).falseby default, and then a requirement that goes round in a circle is an error and an element that invokes itself by name has a:recursion_not_enablederror as well as the unknown-name diagnostic. It also enables guarded recursive item definitions, validating complete finite data and constraints. Alias-only cycles and cycles through function signatures remain errors. Only the caller can saytrue: nothing in a model switches it on. A decision on a requirements cycle is an error either way.:extensions: the additions to DMN 1.6 to compile with (Dmn.Extension), names or modules. None by default. An extension that gives type names (:dmn_1_1_types) adds them as item definitions of the model, after its own and those of the models it imports, so a type that the model defines has the name. An extension that runs external functions (Dmn.Extension.external_functions/0) has thefunctionDefinitionof that kind compiled to the external function of FEEL, and the expressions of the model are prepared with the extensions of FEEL it brings.
Returns {:error, error} when the graph exceeds Dmn.Limits.max_units. Otherwise
{:ok, compiled}: what is wrong with the model is in the diagnostics (Dmn.Compiled.errors/1), and an error of an element
is also an error of its unit, which a run says and which ends a strict run. Dmn.validate/1 is the question whether there is any.