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

The types of a model, as FEEL has them: the item definitions read into a `Feel.ItemDefinitions` (DMN 1.6, 7.3.3), and from them
the type, the allowed values and the type constraints of every variable (`Feel.ItemDefinitions.scope/2`).

`dmn` has no typing of its own. What a `typeRef` means, what an implicit conversion is, and how a value that is not of its type is
made `null` with an error, are FEEL's (DMN 1.6, 10.3.2.9); this module only hands it the definitions as data.

# `constraints`

```elixir
@type constraints() :: String.t() | [Feel.ItemDefinitions.constraint()] | nil
```

What a value of a declared type has to satisfy, as the node of FEEL for a declared value (`Feel.AST`) takes it: `nil` for nothing,
the source of a list of unary tests for the allowed values of the value itself (the common case, and all that a `typeRef` of a
built-in type or of a plain item definition with `allowedValues` has), or the list of every constraint of the type
(`t:Feel.ItemDefinitions.constraint/0`): the allowed values and the type constraint of the type, of the type it refers to, and of
its components, each down to the member it is for.

# `frame`

```elixir
@type frame() :: %{prefix: String.t(), imports: [{String.t(), String.t()}]} | nil
```

How the types of a model are written in the view of another one: the `prefix` that its own item definitions get (the name of the
import and a dot, nothing for an import with no name), and what its `imports` are called in that view, each as the model writes
the name of the import and a dot, and the prefix that the viewer gives the types of the model it names. `nil` for the model
that looks, whose types are as they are.

# `view`

```elixir
@type view() :: %{
  copies: [{Dmn.Model.t(), frame()}],
  frames: %{optional({String.t(), String.t()}) =&gt; frame()}
}
```

What a model sees of the models it imports: the models to read the item definitions of, each with the frame its types are written in,
and the frames by `{namespace, prefix}` (the model and the prefix of an import), for the types of the elements read from them.

# `aliases`

```elixir
@spec aliases([Feel.ItemDefinitions.definition()], view(), %{
  optional(String.t()) =&gt; String.t()
}) :: [
  Feel.ItemDefinitions.definition()
]
```

The item definitions for the type names that the extensions give (`Dmn.Extension.type_aliases/1`), as data for
`Feel.ItemDefinitions.new/1`, to follow the `definitions` of the models of the `view`.

A name is added for every place that the types of a model of the view are written: with the import `Imp` the name `dateTime` is `Imp.dateTime`
too, since a `typeRef` of an imported model is written as the viewer has it (`qualify/2`). It is added where no definition has the
name, so a model that defines `dateTime` has its own type, and the first of the models that share a prefix to define it has it.

    iex> view = %{copies: [{nil, nil}, {nil, %{prefix: "Imp.", imports: []}}], frames: %{}}
    iex> Dmn.Compiler.Types.aliases([%{name: "Imp.dateTime", type_ref: "string"}], view, %{"dateTime" => "date and time"})
    [%{name: "dateTime", type_ref: "date and time"}]

# `declared`

```elixir
@spec declared(Feel.ItemDefinitions.t(), String.t() | nil) ::
  {Feel.Type.t(), constraints()} | nil | {:unread, list()}
```

What a `typeRef` declares, as the node of FEEL's syntax tree for a declared value takes it (`Feel.AST`): `{type, constraints}`,
`nil` for no type, `{:unread, problems}` for one that FEEL cannot read. As for `result/2`, every constraint of the type is kept.

# `definitions`

```elixir
@spec definitions(Dmn.Model.t(), frame()) ::
  {[Feel.ItemDefinitions.definition()], [String.t()]}
```

The item definitions of a model as data for `Feel.ItemDefinitions.new/1`, and the names that were given twice (the first definition
of a name is the one that stands).

With the `frame` of a model that is imported (`view/2`) the definitions are written as the viewer has them: with a prefix of
`"Imp."` the definition `tPerson` is `Imp.tPerson`, and a type it names (`tAddress`) is `Imp.tAddress`.

# `function_outputs`

```elixir
@spec function_outputs([Feel.ItemDefinitions.definition()]) :: %{
  optional(String.t()) =&gt; String.t()
}
```

The `outputTypeRef` of each item definition that is a function type, by the name of the definition, from the data that
`definitions/2` gave (`Feel.ItemDefinitions.new/1` takes the same list). A definition that is the name of another (it has a
`typeRef` and nothing else but its constraints, and is not a collection) is that type, so it has the output of the
function type at the end of the chain of names; a chain that goes round in a circle has none.

    iex> data = [
    ...>   %{name: "F", function: %{output: "tAge", parameters: []}},
    ...>   %{name: "Alias", type_ref: "F"},
    ...>   %{name: "AliasOfAlias", type_ref: "Alias"},
    ...>   %{name: "Loop", type_ref: "Loop"},
    ...>   %{name: "tAge", type_ref: "number"}
    ...> ]
    iex> Dmn.Compiler.Types.function_outputs(data)
    %{"F" => "tAge", "Alias" => "tAge", "AliasOfAlias" => "tAge"}

# `function_type`

```elixir
@spec function_type(
  Feel.ItemDefinitions.t(),
  [Dmn.Model.InformationItem.t()],
  Dmn.Model.InformationItem.t() | nil,
  frame()
) :: Feel.Type.t()
```

A function type from formal parameters and a variable's declared type as the result, in the caller's type frame.
Decision services use `service_type/5` for their final result. Business knowledge models use `knowledge_type/4`
to interpret a function item on their own variable. A type that cannot be read is `Any`.

# `knowledge_type`

```elixir
@spec knowledge_type(
  Feel.ItemDefinitions.t(),
  [Dmn.Model.InformationItem.t()],
  Dmn.Model.InformationItem.t() | nil,
  frame()
) :: Feel.Type.t()
```

`function_type/4` of a business knowledge model, whose variable is the type of the function (`output_declared/3`): when it is a function
item (DMN 1.6, 7.3.3) the result is its `outputTypeRef`, and the parameters stay the formal parameters of the model. The variable of a
decision service describes its own function too; its output decisions are separately kept as value types by `service_type/5`.

# `name`

```elixir
@spec name(String.t()) :: String.t()
```

The canonical name of an element of the model, as the scope of FEEL has it.

# `output_declared`

```elixir
@spec output_declared(Feel.ItemDefinitions.t(), String.t() | nil, %{
  optional(String.t()) =&gt; String.t()
}) ::
  {Feel.Type.t(), constraints()} | nil | {:unread, list()}
```

What the variable of a business knowledge model or a decision service declares for what the function gives (`declared/2`, for the
result). The variable of an invocable is the **type of the function**: an item definition with a `functionItem` (DMN 1.6, 7.3.3),
whose `outputTypeRef` is the type of the result. A model that gives the type of the result directly (`typeRef="number"`) is read so too.

FEEL's function type has the type of the output and none of its constraints, so `function_outputs` (`function_outputs/1`) says
which type each function item names, and the result is held to the constraints of that type as a declared value is.

# `qualify`

```elixir
@spec qualify(String.t() | nil, frame()) :: String.t() | nil
```

A type reference as the viewer writes it, given the frame of the model that wrote it: the prefix of the model before a type of
its own, the viewer's prefix for the model that a type is of through an import of the model. A built-in type, no type, and the
type of the model that looks (`nil`) are as they are.

    iex> frame = %{prefix: "Imp.", imports: [{"Other.", "Other."}]}
    iex> Dmn.Compiler.Types.qualify("tPerson", frame)
    "Imp.tPerson"
    iex> Dmn.Compiler.Types.qualify("Other.tAddress", frame)
    "Other.tAddress"
    iex> Dmn.Compiler.Types.qualify("number", frame)
    "number"
    iex> Dmn.Compiler.Types.qualify("list<number!>", frame)
    "list<number!>"
    iex> Dmn.Compiler.Types.qualify("tPerson", nil)
    "tPerson"
    iex> Dmn.Compiler.Types.qualify(nil, frame)
    nil

# `result`

```elixir
@spec result(Feel.ItemDefinitions.t(), Dmn.Model.InformationItem.t() | nil) ::
  {Feel.Type.t() | nil, constraints()} | {:unread, list()}
```

The declared type of the value of a variable and what it has to satisfy, for the result of a decision: `{type, constraints}`,
with `nil` for no type and no constraints. A type that FEEL cannot read is `{:unread, problems}`.

All the constraints of the type are kept (D-161 of `feel`): the allowed values of a component, the type constraint, a collection's
allowed values for its elements, a second list of allowed values. The result of an expression is verified against all of them, and
none is left out.

# `service_type`

```elixir
@spec service_type(
  Feel.ItemDefinitions.t(),
  [Dmn.Model.InformationItem.t()],
  [Dmn.Model.Decision.t()],
  Dmn.Model.InformationItem.t() | nil,
  frame()
) :: Feel.Type.t()
```

A service's result after its output decisions and its own declaration have been enforced.
Several outputs form a context by decision name. The service variable describes the function;
only that declaration unwraps a function layer, preserving function-valued output decisions.

# `view`

```elixir
@spec view(Dmn.Model.t(), %{optional(String.t()) =&gt; Dmn.Model.t()}) :: view()
```

What `model` sees of the models it imports (and that they import), each of them **once** (`models` are by namespace).

The item definitions of a model that is imported are those of the importing model under the name of the import: with the
import `Imp`, the definition `tPerson` is `Imp.tPerson`, and a type that it names (`tAddress`) is `Imp.tAddress`, since that is what
the importing model calls it. A model that `model` does not import but a model it imports does is not named by `model` (DMN 1.6,
6.3.3: an import is explicit), but its types are needed for the types of the model that names it, so they are read once, under a
prefix that no model can write (U+0001, the namespace and U+0001, as the other names that the compiler makes), and a type that
an imported model names through an import of its own is that, whichever way it was reached.

Under the path of imports instead, a model that is reached by two paths is read twice: two models of two imports each, 15 levels deep,
are 32,768 copies of one definition (79 s). An import with no name is read where the model that has it is, since the types of the
model are written without a prefix.

`copies` are in the order of the reading, `model` itself first.

---

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