The conversion of bound values to the types that were declared for them (D-138).
A Feel.Scope says what type each name has. When a run starts, Feel.Analysis.Analyzer's
:conform_bindings node has each value that is bound to a name the expression reads
converted to its declared type by the implicit conversions of DMN 1.6, section 10.3.2.9.4, and
verified to conform to it, as the result is (Feel.Runtime.ResultType, D-138). A backend calls
conform_bindings/3. It is a function of the runtime, and not a part of a backend, because
generated code may call only Feel.Runtime and both backends have to give the same result.
nullconforms to every type, and is the value as it is. So does a name that is not bound, which is left for its read to report. A type marked!(D-48) is the exception:nulldoes not conform tonumber!, and neither does a name that has no value. In the mode:enforcethat ends the run with an error of kind:input, in either mode of the run, because the type says that the value is there and the expression was analyzed on it. In the mode{:report, severity}it is reported with that severity and the value stays.- A value that conforms is the value. One that does after a conversion is converted
(
Feel.Type.Conversion.convert/3: conformance first, then a singleton list is unwrapped, a value is wrapped in a list, a date becomes a date and time), and the conversion is reported as an:info(:implicit_conversion, D-46), which does not end a strict run. - A value that cannot be made to conform is
null, with an error (:type_mismatch) that ends a run in strict mode. For a type marked!it cannot benulleither, so it ends the run with an error of kind:input, as anulldoes.
This is what lets the type of a name be relied on: every read sees a value of its type, or
null. That is the mode :enforce. In the mode {:report, severity} (D-138) nothing is
converted and nothing becomes null: a value that does not conform as it is gets a
:type_mismatch diagnostic with that severity, and stays. The type is then an assertion of the
caller's, and the report says when it does not hold.
Summary
Types
What is done with a value that does not conform: :enforce converts it or makes it null, and
{:report, severity} leaves it and reports it with that severity (D-138).
Functions
Converts the bound values of the names in conforms to their types.
Types
Functions
@spec conform_bindings( Feel.Runtime.Env.t(), Feel.bindings(), [{Feel.Name.t(), Feel.Diagnostic.span(), Feel.Type.t()}], mode() ) :: {Feel.bindings(), Feel.Runtime.Env.t()}
Converts the bound values of the names in conforms to their types.
Each entry is the canonical name, the span of its first read in the expression, which goes on the
error, and the declared type, with the names of types resolved. Returns the bindings that the
expression is to run with, and the environment. mode is :enforce, the default, or
{:report, severity}.
iex> env = Feel.Runtime.Env.new()
iex> conforms = [{"age", {0, 3}, :number}, {"tags", {4, 8}, {:list, :string}}]
iex> bindings = %{"age" => [Decimal.new(30)], "tags" => "a", "id" => 7}
iex> {converted, converted_env} = Feel.Runtime.BindingTypes.conform_bindings(env, bindings, conforms)
iex> converted
%{"age" => Decimal.new(30), "tags" => ["a"], "id" => 7}
iex> for d <- Feel.Runtime.Env.diagnostics(converted_env), do: {d.severity, d.code, d.span}
[{:info, :implicit_conversion, {0, 3}}, {:info, :implicit_conversion, {4, 8}}]
iex> {converted, env} = Feel.Runtime.BindingTypes.conform_bindings(env, %{"age" => "x"}, conforms)
iex> converted
%{"age" => nil}
iex> [diagnostic] = Feel.Runtime.Env.diagnostics(env)
iex> {diagnostic.code, diagnostic.span, diagnostic.message}
{:type_mismatch, {0, 3}, "the value of \"age\" is a string, which cannot be converted to the declared type number"}