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

How values relate to types (DMN 1.6, sections 10.3.2.9 and 10.3.2.15, Tables 43
and 62).

`of/1` gives the type of a value. `value_conforms?/2` asks whether a value fits a
type, element by element, and `instance?/2` is the `instance of` operator, which
differs only in how it treats `null`.

## `null`

`null` conforms to every type, because `Null <: T`. That is what lets missing data
flow through typed variables and parameters, and what makes a context member set to
`null` fit its type. But `null instance of number` is false, and so is
`null instance of Any`: Table 62 makes `null` an instance of `Null` and nothing
else. That rule is in `instance?/2` alone, so it can be changed in one place.

## Missing members

A value fits a context type only if every member of the type is present in the
value. A member that is absent does not conform, while one that is present with the
value `null` does.

Guarded recursive descriptors are unfolded while visiting the complete finite value, without
the analysis projection's node cutoff. Allowed values and type constraints are separate declaration
checks, not part of structural membership. Functions are checked through their retained runtime
signatures and conservative type conformance, not through a structural result approximation.

The functions work on resolved types; see `Feel.Type.resolve/2`.

# `instance?`

```elixir
@spec instance?(Feel.Value.t(), Feel.Type.t()) :: boolean()
```

The `instance of` operator (Table 62): returns `true` when `value` is an instance
of `type`.

Implicit conversions do not apply, so a singleton list is not an instance of the
type of its element.

    iex> Feel.Type.Instance.instance?([Decimal.new(123)], {:list, :number})
    true
    iex> Feel.Type.Instance.instance?(Decimal.new(123), :string)
    false
    iex> Feel.Type.Instance.instance?(nil, :null)
    true
    iex> Feel.Type.Instance.instance?(nil, :number)
    false
    iex> Feel.Type.Instance.instance?(Decimal.new(1), {:list, :number})
    false

# `of`

```elixir
@spec of(Feel.Value.t()) :: Feel.Type.t()
```

Returns the type of a value (Table 43).

  * A list has the type `list<T>` where `T` is the join of the types of its
    elements (see `Feel.Type.Conformance.join/2`). The empty list is `list<Null>`,
    and a list of unrelated elements is `list<Any>`.
  * A context has one member per entry.
  * A range has the type `range<T>` where `T` is the type of its endpoints; an
    undefined endpoint is ignored.
  * A function has the type of its signature.
  * A term that is not a value of FEEL (a float or an atom that a host bound, D-39), alone or in a list or a context,
    has the type `Any`: `Feel.eval/3` makes the scope of its bindings from their types.

## Examples

    iex> Feel.Type.Instance.of(Decimal.new(1))
    :number
    iex> Feel.Type.Instance.of(["a", "b"])
    {:list, :string}
    iex> Feel.Type.Instance.of(["a", true, Decimal.new(123)])
    {:list, :any}
    iex> Feel.Type.Instance.of(%{"name" => "Peter", "age" => Decimal.new(30)})
    {:context, %{"age" => :number, "name" => :string}}
    iex> Feel.Type.Instance.of([:foo])
    {:list, :any}

# `value_conforms?`

```elixir
@spec value_conforms?(Feel.Value.t(), Feel.Type.t()) :: boolean()
```

Returns `true` when `value` fits `type`: its type conforms to `type`, checked
element by element so that no join is needed.

`null` fits every type, a list fits `list<T>` when all its elements fit `T`
(the empty list fits every list type), a context fits `context<N>` when it has
every member of `N` with a fitting value, and a range fits `range<T>` when its
defined endpoints fit `T`. A function fits a function type when its signature
conforms to it.

    iex> Feel.Type.Instance.value_conforms?(nil, :number)
    true
    iex> Feel.Type.Instance.value_conforms?(nil, {:non_null, :number})
    false
    iex> Feel.Type.Instance.value_conforms?(Decimal.new(1), {:non_null, :number})
    true
    iex> Feel.Type.Instance.value_conforms?([], {:list, :string})
    true
    iex> Feel.Type.Instance.value_conforms?(%{"a" => nil}, {:context, %{"a" => :number}})
    true
    iex> Feel.Type.Instance.value_conforms?(%{}, {:context, %{"a" => :number}})
    false

---

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