# `Feel.Builtin.Function`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/builtin/function.ex#L1)

A built-in function of FEEL, as the registry describes it (D-56, which settles D-56).

The area modules under `Feel.Builtin` list their functions with `functions/0`, and
`Feel.Builtin.function/1` finds one by name. A function has:

  * a `name`, canonical (see `Feel.Name`).
  * `signatures`: each a list of **parameters** (a name, the types it accepts, and whether it
    is optional) and the types that the result can be. The names let a call with named
    arguments be put in order (`Feel.Analysis.Calls`); the number of parameters, and how many are
    optional, decide how many arguments a call may have. The types are the description of the
    function and what the analyzer types a call by: whether an argument is one of them is
    decided by the function, when it runs.
  * a `result_from` of a signature, if the result depends on the **types of the arguments** and not only on the
    signature (D-151): `max(xs)` of a `list<number>` is a number and `append(xs, "a")` is a list of numbers and
    strings, where the types of the result say `Any` and `list<Any>`. It says how the type of the result is made
    from the types of the arguments (`t:result_from/0`), and `Feel.Analysis.Calls.result_type/3` makes it.
  * a `same_kind` of a signature, if some of its arguments have to be of one kind that has an order and not only each of
    one that its parameter takes (D-155): the two points of `before(point1, point2)`, the items of `max(c1, c2)`. A
    parameter says what one argument may be, and this says that the arguments agree, which `Feel.Analysis.Calls`
    checks (`t:same_kind/0`).
  * `call`: the function itself. It takes the environment of the run (`Feel.Runtime.Env`, for the
    clock that `now()` reads) and the list of its arguments, already evaluated and in order, and
    returns an outcome (`t:Feel.Runtime.Env.outcome/0`): `{:ok, value}`, `:missing` for a `null`
    that is not a mistake, or `{:error, diagnostic}` for a `null` that is. It is called only with
    a number of arguments that some signature accepts.
  * `pure?`: whether the result depends on the arguments alone. A call of a pure function
    with constants is folded by the analyzer and the optimizer; `now()` and `today()` read the
    clock, so they are not.
  * `never_null?`: whether the result is never `null` for an argument of a signature, as the result
    of `is`. The analyzer types the result of any other function with a `null`.
  * `check_arguments?`: whether an argument that no signature takes is `null` with an
    `:argument_out_of_domain` error before the function is called, which the analyzer knows
    (`Feel.Analysis.Calls.mismatch/3`) and a call by name checks against its signature (D-56). It is `false`
    for a function whose own error for a wrong kind is another, as `not`, whose error is a type mismatch like
    the operators': the function says it, and what it says does not change.

# `argument_count`

```elixir
@type argument_count() :: non_neg_integer() | {:at_least, non_neg_integer()}
```

A number of arguments that a function takes: exactly that many, or that many and more (the arguments
of a variadic parameter).

# `call_outcome`

```elixir
@type call_outcome() :: Feel.Runtime.Env.outcome() | :next
```

What a function returns. An overload of an extension (`with_overloads/2`) may also say `:next`: the
arguments are not for it. `:next` does not leave `Feel.Builtin.call/4`: the overloads are tried by `with_overloads/2`, and
a function that is not an overload and says it has the arguments refused, as an error of their domain.

# `kind_slot`

```elixir
@type kind_slot() :: {:kinds, [atom()]} | :free
```

What an argument that has to be of one kind may be of (`t:same_kind/0`): `{:kinds, kinds}` the kinds that have an order
that it can be of, which is none for a value that has no order, or `:free` if it is of no kind in particular (it may be
`null`, or anything). The analyzer says it of the type of an argument, and the run of its value.

# `parameter`

```elixir
@type parameter() :: %{
  :name =&gt; String.t(),
  :types =&gt; [Feel.Type.t(), ...],
  :optional? =&gt; boolean(),
  optional(:variadic?) =&gt; true,
  optional(:quiet?) =&gt; true,
  optional(:integer?) =&gt; true
}
```

# `result_from`

```elixir
@type result_from() ::
  {:list_of, [{:elements | :flat | :items, non_neg_integer()}, ...]}
  | {:list_of, [{:elements | :flat | :items, non_neg_integer()}, ...],
     [{:elements | :items, non_neg_integer()}]}
  | {:list_of, [{:elements | :flat | :items, non_neg_integer()}, ...],
     [{:elements | :items, non_neg_integer()}],
     :always | [{:elements, non_neg_integer()}]}
  | {:element_of, non_neg_integer()}
  | {:item_of, non_neg_integer()}
```

How the type of a result is made from the types of the arguments (D-151), by the places of the arguments (from 0; a
place at or after a variadic parameter is that argument and all that follow):

  * `{:list_of, parts}` and `{:list_of, parts, non_empty_from}`: a list of the elements that the parts give: `{:elements, place}` the elements of a list (a
    value that is not a list is the list of it, section 10.3.2.9.4), `{:flat, place}` the same with the lists inside
    them taken apart as `flatten` does, and `{:items, place}` the arguments themselves, as the items they are.
  * `non_empty_from` of a `:list_of` (D-152) lists the places that make the result a list that has an element: `{:elements, place}`
    when the argument there has one (a list that has an element, or a value, which is the list of itself), `{:items, place}`
    when an item is given there.
  * a fourth element of a `:list_of` says when the call is `null` (D-152): `:always` (a position out of range, a function that is
    not one, ...) or the places `{:elements, place}` whose argument being `null` makes it so, for a function that fails for
    nothing else (`reverse`, `append`, ...); the result is then not `null` when none of those may be.
  * `{:element_of, place}`: one of the elements of the list, or `null` if there is none (`max`, `min`); not `null` when the list has an
    element and they are of one kind that is ordered.
  * `{:item_of, place}`: one of the arguments (`max(a, b, c)`).

# `same_kind`

```elixir
@type same_kind() :: [{:point | :item_of | :elements, non_neg_integer()}, ...]
```

The arguments of a call that have to be of one kind that has an order (D-155), by the places of the arguments (from 0; a
place at or after a variadic parameter is that argument and all that follow): a number, a string, a date, a time, a
date and time, or a duration of one of the two kinds. The call is out of the domain when the kinds that the arguments
may be of have nothing in common, as `before("a", 2)` is. A part is:

  * `{:point, place}`: the argument at the place, as a point, which is the value itself or the list of one element that is
    it (section 10.3.2.9.4): `before(point1, point2)`.
  * `{:item_of, place}`: the arguments from the place on, as the items of `max(c1, ..., cn)`: each is an item, except
    for one argument alone, which is the list of items if it is a list (as for `result_from`).
  * `{:elements, place}`: the elements of the list at the place, which are the items of `max(list)`.

An argument that is `null`, of any type, or of the name of a model type, is of no kind in particular and agrees with all.

# `signature`

```elixir
@type signature() :: %{
  :parameters =&gt; [parameter()],
  :result =&gt; [Feel.Type.t(), ...],
  optional(:result_from) =&gt; result_from(),
  optional(:same_kind) =&gt; same_kind(),
  optional(:extension) =&gt; true
}
```

A signature. One that an extension adds (`Feel.Extension`, D-81) has `extension: true`.

# `t`

```elixir
@type t() :: %Feel.Builtin.Function{
  arity_index: %{required(non_neg_integer()) =&gt; [signature()]} | nil,
  call: (Feel.Runtime.Env.t(), [Feel.Value.t()] -&gt; call_outcome()),
  check_arguments?: boolean(),
  name: Feel.Name.t(),
  never_null?: boolean(),
  pure?: boolean(),
  signatures: [signature(), ...]
}
```

# `accepts?`

```elixir
@spec accepts?(t(), non_neg_integer()) :: boolean()
```

Returns `true` when a call with this many arguments is one that some signature accepts.

# `agree?`

```elixir
@spec agree?([kind_slot()]) :: boolean()
```

Whether the arguments, as the kinds they may be of, agree: some kind that has an order is the kind of every one of
them that is not `:free`. A single argument that cannot be of such a kind (it has no order) does not agree with
itself, and no argument that is not free is one that agrees.

    iex> Feel.Builtin.Function.agree?([{:kinds, [:number, :string]}, {:kinds, [:string]}, :free])
    true
    iex> Feel.Builtin.Function.agree?([{:kinds, [:number]}, {:kinds, [:string]}])
    false
    iex> Feel.Builtin.Function.agree?([{:kinds, [:number, :string]}, {:kinds, [:number]}, {:kinds, [:string]}])
    false
    iex> Feel.Builtin.Function.agree?([{:kinds, []}])
    false
    iex> Feel.Builtin.Function.agree?([:free, :free])
    true

# `arguments_at`

```elixir
@spec arguments_at(signature(), [argument], non_neg_integer()) :: [argument]
when argument: var
```

The arguments (or the values) that are at a place of a signature: the one at the place, and for a variadic parameter all
that follow. A parameter that the call leaves out has none.

    iex> {:ok, sum} = Feel.Builtin.function("sum")
    iex> [list, items] = sum.signatures
    iex> Feel.Builtin.Function.arguments_at(list, [1, 2, 3], 0)
    [1]
    iex> Feel.Builtin.Function.arguments_at(items, [1, 2, 3], 0)
    [1, 2, 3]
    iex> Feel.Builtin.Function.arguments_at(list, [1], 3)
    []

# `arities`

```elixir
@spec arities(t()) :: [argument_count()]
```

The numbers of arguments that a call may have: for each signature, from its required parameters to
all of them. A signature with a variadic parameter takes `{:at_least, n}`, and that covers the numbers
from `n` of the others.

    iex> {:ok, time} = Feel.Builtin.function("time")
    iex> Feel.Builtin.Function.arities(time)
    [1, 3, 4]

# `element_spec`

```elixir
@spec element_spec(Feel.Type.t()) :: Feel.Type.t()
```

The type of the elements of a type of a parameter that is a list.

    iex> Feel.Builtin.Function.element_spec({:list, :string})
    :string

# `extensions_for_names`

```elixir
@spec extensions_for_names(t(), [String.t()]) :: [signature()]
```

The signatures of extensions that have a parameter of each of the names (D-81). A call by name is for one signature,
the one `for_names/2` chooses, but an extension may take the call all the same, with the same names for other kinds of
argument; a call that one fits is the extension's to answer.

    iex> {:ok, date} = Feel.Builtin.function("date")
    iex> Feel.Builtin.Function.extensions_for_names(date, ["year", "month", "day"])
    []

# `for_names`

```elixir
@spec for_names(t(), [String.t()]) :: {:ok, signature()} | :error
```

The signature that a call with named arguments is for: the first one that has a parameter of each
of the names. The call is then put in the order of its parameters, and a parameter that the call
leaves out is `null` (see `Feel.Analysis.Calls`).

Names have no order in a call, but two signatures can have the same names in different orders, as
`before(point, range)` and `before(range, point)` do, and what the function says differs. Of the
signatures that fit, the first that has the parameters in the order in which the names are written is
the one; if there is none, the first that fits.

A signature that has exactly the names of the call goes before one that has more (D-81): the overload
`date and time(date, timezone)` of an extension is the one for `date: ..., timezone: ...`, and not the
signature `(date, time, timezone)` of DMN 1.6 with the `time` left out. This is only a choice among the
signatures that fit, and it is the same as before where none has exactly the names.

Signatures that differ only in types are one signature here (`merge_variants/1`): the names do not say of what kind
the arguments are.

    iex> {:ok, time} = Feel.Builtin.function("time")
    iex> {:ok, signature} = Feel.Builtin.Function.for_names(time, ["hour", "minute", "second"])
    iex> Enum.map(signature.parameters, & &1.name)
    ["hour", "minute", "second", "offset"]
    iex> Feel.Builtin.Function.for_names(time, ["hour", "hours"])
    :error
    iex> {:ok, before} = Feel.Builtin.function("before")
    iex> {:ok, signature} = Feel.Builtin.Function.for_names(before, ["range", "point"])
    iex> Enum.map(signature.parameters, & &1.name)
    ["range", "point"]

# `index_arities`

```elixir
@spec index_arities(t()) :: t()
```

Returns the function with the signatures that take each number of arguments, up to 12, kept in it (D-56): the answer of
`accepts?/2` and `with_arity/2` is then a lookup and not a pass over the signatures, each of which works out its numbers
of arguments from its parameters. `Feel.Builtin.function/1` does this once for the table it keeps. A function whose
signatures are changed has to lose it (`with_overloads/2` does), since the index is of the signatures it was made from.

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> abs.arity_index |> Map.fetch!(1) |> length()
    3
    iex> abs.arity_index |> Map.fetch!(2)
    []

# `merge_variants`

```elixir
@spec merge_variants([signature()]) :: [signature()]
```

The signatures with those that differ only in types made one: the same parameters (the names, and whether each is
optional, variadic, quiet or an integer) taking the types of all of them, and the same result with the types of all.

A call by name sees a function so. Its names say which parameters the call has and not of what kinds they are, so
`abs(n: x)` is for the three kinds that `abs` takes, and not for the first signature that has an `n`. The signatures of
an extension are never made one (D-81), and a signature that differs from every other in its parameters is as it was.

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> [merged] = Feel.Builtin.Function.merge_variants(abs.signatures)
    iex> Enum.map(merged.parameters, & &1.types)
    [[:number, :days_and_time_duration, :years_and_months_duration]]
    iex> merged.result
    [:number, :days_and_time_duration, :years_and_months_duration]
    iex> {:ok, date} = Feel.Builtin.function("date")
    iex> Feel.Builtin.Function.merge_variants(date.signatures) == date.signatures
    true

# `ordered_kinds`

```elixir
@spec ordered_kinds() :: [atom(), ...]
```

The kinds of value that have an order (Table 55), which is what a `same_kind` is about: a number, a string, a date, a
time, a date and time, and a duration of either kind. A boolean, a list, a context, a range and a function have none.

    iex> :date in Feel.Builtin.Function.ordered_kinds()
    true
    iex> :boolean in Feel.Builtin.Function.ordered_kinds()
    false

# `parameter`

```elixir
@spec parameter(String.t(), [Feel.Type.t(), ...], keyword()) :: parameter()
```

A parameter: its name, the types it accepts, and `optional: true` if a call may leave it out.

A parameter that is `variadic: true` stands for all the arguments from its place on, so it is the last
of its signature (`append(list, item...)`, `sum(n1...)`). It takes one argument at least, and none if it
is also `optional: true`. The map has the key `:variadic?` only for such a parameter.

A parameter that is `quiet: true` is read without reporting (D-100): what is wrong in the argument, a name that is not
bound, a path through an entry that is not there, is not reported, neither when the expression is prepared nor when it
is run, and does not end a `mode: :strict` run. The argument is `null` then, as it is without the flag. The map has the
key `:quiet?` only for such a parameter.

A parameter that is `integer: true` wants an integer: a number with a fractional part is cut to one before the function
looks at it ("from decimal to integer", DMN 1.6 section 10.3.2.9.4, `Feel.Builtin.Arguments.integer/1`), and the run
reports that (D-46). The map has the key `:integer?` only for such a parameter.

    iex> Feel.Builtin.Function.parameter("offset", [:days_and_time_duration, :null], optional: true)
    %{name: "offset", types: [:days_and_time_duration, :null], optional?: true}
    iex> Feel.Builtin.Function.parameter("item", [:any], optional: true, variadic: true)
    %{name: "item", types: [:any], optional?: true, variadic?: true}
    iex> Feel.Builtin.Function.parameter("value", [:any], quiet: true)
    %{name: "value", types: [:any], optional?: false, quiet?: true}
    iex> Feel.Builtin.Function.parameter("scale", [:number], integer: true)
    %{name: "scale", types: [:number], optional?: false, integer?: true}

# `parameter_at`

```elixir
@spec parameter_at(signature(), non_neg_integer()) :: parameter() | nil
```

The parameter that the argument at this place (from 0) is for: the one at that place, or for an argument past the
last, the variadic parameter that ends the signature; `nil` if there is none.

    iex> {:ok, sum} = Feel.Builtin.function("sum")
    iex> [_list, variadic] = sum.signatures
    iex> Feel.Builtin.Function.parameter_at(variadic, 5).name
    "n1"
    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> Feel.Builtin.Function.parameter_at(hd(abs.signatures), 1)
    nil

# `quiet_at?`

```elixir
@spec quiet_at?([signature()], non_neg_integer()) :: boolean()
```

Whether the argument at this place (from 0) is read quietly: every one of the signatures has a quiet parameter there.

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> Feel.Builtin.Function.quiet_at?(abs.signatures, 0)
    false
    iex> Feel.Builtin.Function.quiet_at?([], 0)
    false

# `same_kind`

```elixir
@spec same_kind(signature(), same_kind() | nil) :: signature()
```

The signature with the arguments that have to be of one kind (`t:same_kind/0`, D-155). `nil` says there are none.

    iex> points = [Feel.Builtin.Function.parameter("point1", [:number, :string]), Feel.Builtin.Function.parameter("point2", [:number, :string])]
    iex> signature = Feel.Builtin.Function.signature(points, [:boolean])
    iex> Feel.Builtin.Function.same_kind(signature, [{:point, 0}, {:point, 1}]).same_kind
    [point: 0, point: 1]
    iex> Feel.Builtin.Function.same_kind(signature, nil) == signature
    true

# `signature`

```elixir
@spec signature([parameter()], [Feel.Type.t(), ...], result_from() | nil) ::
  signature()
```

A signature: its parameters and the types of its result, and how the result follows the types of the arguments if it does
(`t:result_from/0`).

    iex> param = Feel.Builtin.Function.parameter("from", [:string])
    iex> Feel.Builtin.Function.signature([param], [:date])
    %{parameters: [%{name: "from", types: [:string], optional?: false}], result: [:date]}
    iex> list = Feel.Builtin.Function.parameter("list", [{:list, :any}])
    iex> Feel.Builtin.Function.signature([list], [{:list, :any}], {:list_of, [{:elements, 0}]}).result_from
    {:list_of, [{:elements, 0}]}

# `spec_kind`

```elixir
@spec spec_kind(Feel.Type.t()) :: atom()
```

The kind that a type of a parameter stands for: a primitive type is itself, and a list, a range, a context and
a function are `:list`, `:range`, `:context` and `:function`, whatever they are of. `:any` takes every kind.

    iex> Feel.Builtin.Function.spec_kind({:list, :string})
    :list
    iex> Feel.Builtin.Function.spec_kind(:number)
    :number

# `variadic?`

```elixir
@spec variadic?(parameter()) :: boolean()
```

Returns `true` for a variadic parameter.

# `variants_of`

```elixir
@spec variants_of(t(), signature()) :: [signature()]
```

The signatures that a signature from `for_names/2` stands for: those of the function that differ from it only in types
(`merge_variants/1`), or itself if it is one of an extension. A call by name is typed by each of them.

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> {:ok, signature} = Feel.Builtin.Function.for_names(abs, ["n"])
    iex> length(Feel.Builtin.Function.variants_of(abs, signature))
    3

# `with_arity`

```elixir
@spec with_arity(t(), non_neg_integer()) :: [signature()]
```

The signatures that accept this many arguments.

    iex> {:ok, date} = Feel.Builtin.function("date")
    iex> [signature] = Feel.Builtin.Function.with_arity(date, 3)
    iex> Enum.map(signature.parameters, & &1.name)
    ["year", "month", "day"]

# `with_overloads`

```elixir
@spec with_overloads(t(), [t()]) :: t()
```

The function with the overloads of extensions added to it (`Feel.Extension`, D-81): its signatures are those
of the function and then those of the overloads, in the order of the extensions.

The function runs first. If it reports an error, or if none of its signatures takes the number of
arguments, the overloads that take it are tried, in order, and the first that does not answer `:next` is the
result. If none does, the result is the error of the function, which is the one it would give without
the overloads (`Feel.Builtin.arity_mismatch/2` when its signatures did not fit). A `null` that the function
gives because an argument is `null` (`:missing`) is a result, and the overloads do not see it. So what was a
result before is the result still.

The function is as pure as all of them are, and as free of `null` as all of them are.

    iex> {:ok, number} = Feel.Builtin.function("number")
    iex> overload = %Feel.Builtin.Function{
    ...>   name: "number",
    ...>   signatures: [
    ...>     Feel.Builtin.Function.signature(
    ...>       [Feel.Builtin.Function.parameter("from", [:string]), Feel.Builtin.Function.parameter("group", [:string])],
    ...>       [:number]
    ...>     )
    ...>   ],
    ...>   call: fn _env, [_from, _group] -> {:ok, Decimal.new(2)} end
    ...> }
    iex> merged = Feel.Builtin.Function.with_overloads(number, [overload])
    iex> Feel.Builtin.Function.arities(number)
    [1, 3]
    iex> Feel.Builtin.Function.arities(merged)
    [1, 2, 3]
    iex> merged.call.(Feel.Runtime.Env.new(), ["1", ","])
    {:ok, Decimal.new(2)}
    iex> merged.call.(Feel.Runtime.Env.new(), ["1.5"])
    {:ok, Decimal.new("1.5")}

---

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