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.
Summary
Functions
The instance of operator (Table 62): returns true when value is an instance
of type.
Returns the type of a value (Table 43).
Returns true when value fits type: its type conforms to type, checked
element by element so that no join is needed.
Functions
@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
@spec of(Feel.Value.t()) :: Feel.Type.t()
Returns the type of a value (Table 43).
- A list has the type
list<T>whereTis the join of the types of its elements (seeFeel.Type.Conformance.join/2). The empty list islist<Null>, and a list of unrelated elements islist<Any>. - A context has one member per entry.
- A range has the type
range<T>whereTis 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/3makes 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}
@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