# `Dmn.Extension`
[🔗](https://github.com/FeeJai/BDM/blob/dmn-v0.1.0/apps/dmn/lib/dmn/extension.ex#L1)

A set of additions to DMN 1.6 that a caller switches on when it compiles a model (DD-28), as `Feel.Extension` is for FEEL (D-81 of `feel`).

DMN 1.6 defines what a model means, and the models that exist were written for other versions and other engines. An extension is a
module that says what it adds, and the compiler asks the extensions that are on at the few places where it decides. Nothing in the
compiler names an extension.

    Dmn.compile(model, extensions: [:dmn_1_1_types])

The option takes names (`registry/0`) and modules that implement this behaviour. It is `[]` by default, and then the engine is DMN 1.6
and nothing else. The extensions are those of the compile: they are not a property of a run, and the compiled model does not hold them.
A name that is not one, a module that is not an extension, and two extensions that give one name two meanings are
`{:error, %Dmn.Error{kind: :compile, code: :invalid_options}}`, as the options of a run are.

## What an extension can do

An extension **adds**: it never changes what a model that is valid without it means. What it adds was an error before (a `typeRef`
that names no type), and a name that the model defines itself is the model's, whatever an extension says of it. The DMN TCK is run
with the extensions of `Dmn.TCK` on, and a node that is exact without them must stay exact.

| Callback | Adds |
| -------- | ---- |
| `c:type_aliases/0` | names of types that stand for another type, such as the names that DMN 1.1 gave the date and time types |
| `c:feel_extensions/0` | the `Feel.Extension`s that the expressions of the model are prepared with |
| `c:external_functions/0` | the kinds of external function that the extension runs (`:java`), each with the kind of FEEL (`"java"`) that it is run as |

## External functions

A `functionDefinition` of a kind other than FEEL (`kind="Java"`) describes a function that something outside the model runs, and DMN 1.6
does not say how. The engine does not run one: the function is `null` and an `:unsupported_function_kind` error. An extension that
names the kind in `c:external_functions/0` has it read as the external function of FEEL of that kind, with the body of the definition
for the description, and the function of FEEL extension that runs that kind (`Feel.Analysis.External`, D-211 of `feel`) is the one
that `c:feel_extensions/0` brings. `dmn` does not know any kind: `dmn_java` is the one that runs Java.

# `external_functions`
*optional* 

```elixir
@callback external_functions() :: %{optional(:java | :pmml | :onnx) =&gt; String.t()}
```

The kinds of external function that the extension runs: the `kind` of a `functionDefinition` (`:java`, `:pmml`, `:onnx`) to the kind
that the function is in FEEL, the key of `function(...) external {kind: ...}` (`"java"`). A function of such a kind is compiled to
the external function of FEEL, which the extensions of FEEL that the extension brings have to run (`c:feel_extensions/0`).

# `feel_extensions`
*optional* 

```elixir
@callback feel_extensions() :: [atom()]
```

The extensions of FEEL that the expressions of the model are prepared with (`Feel.Extension`: names or modules). Those of all the
extensions that are on are given together.

# `name`

```elixir
@callback name() :: atom()
```

The name of the extension, for the messages about it.

# `type_aliases`
*optional* 

```elixir
@callback type_aliases() :: %{required(String.t()) =&gt; String.t()}
```

Type names that stand for a type of FEEL: the name that is written in a `typeRef`, to the name of the type it stands for
(`Feel.Type.builtin/1`). A model that defines an item definition of a name is not asked: its definition is the type.

# `external_functions`

```elixir
@spec external_functions([module()]) :: %{optional(atom()) =&gt; String.t()}
```

The external function kinds that the extensions that are on run: the kind of the `functionDefinition` to the kind in FEEL. A later
extension wins for a kind that two give.

# `feel_extensions`

```elixir
@spec feel_extensions([module()]) :: [atom()]
```

The extensions of FEEL that the extensions that are on bring, each once, in the order given.

# `registry`

```elixir
@spec registry() :: %{optional(atom()) =&gt; module()}
```

The extensions that `extensions:` knows by name, each with the module it stands for.

    iex> Dmn.Extension.registry()
    %{dmn_1_1_types: Dmn.Extension.Dmn11Types}

# `resolve`

```elixir
@spec resolve(term(), %{optional(atom()) =&gt; module()}) ::
  {:ok, [module()]} | {:error, String.t()}
```

The modules for a list of names and modules, each once, in the order given. The names are those of `registry/0`, or of the map
given as the second argument.

    iex> Dmn.Extension.resolve([:dmn_1_1_types, Dmn.Extension.Dmn11Types])
    {:ok, [Dmn.Extension.Dmn11Types]}
    iex> {:error, message} = Dmn.Extension.resolve([:nope])
    iex> message =~ ":nope"
    true

# `type_aliases`

```elixir
@spec type_aliases([module()]) :: %{optional(String.t()) =&gt; String.t()}
```

The type aliases of the extensions that are on, from the name to the type it stands for. `resolve/1` has seen that no two give one
name two types.

---

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