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

The FEEL type `function` (DMN 1.6, section 10.3.2.8).

Functions are ordinary values: a decision model supplies business knowledge
models and decision services as functions in the scope an expression runs in,
and a host application can supply Elixir functions the same way. There are
three kinds:

  * `:feel` - a function defined in FEEL with `function(...)`. It closes over
    the scope where it was defined.
  * `:builtin` - one of the built-in functions of the specification, which a name stands for where nothing else has
    it (D-82). It has the signatures of the function, and a call finds the function by its name. One that a declared
    function type was given in place of a function made of several signatures (`narrowings/2`, D-197) has one
    signature, and a call of it takes the arguments as a function of FEEL with those parameters does.
  * `:native` - an Elixir function that the host application exposes. It is
    called with its arguments converted at the boundary to the Elixir types
    named in its signature.

The type of a function is `function<T1, ..., Tn> -> R`. Parameter names are not
part of it. A literal FEEL function's result descriptor comes from kind-only analysis in its
written environment and is preserved across analysis tiers, call specialization and optimization
(D-219). Its precise body call result is separate analysis information, not runtime metadata.
Signature inspection and callback checks use the descriptor; they do not inspect the implementation.

Treat the struct as opaque: build native functions with `native/2` and read
them through the functions of this module. A built-in function has one type for each number of arguments it takes
(`types_at/2`), since a function type has a fixed number of parameters and `date` takes one argument or three.

# `boundary_type`

```elixir
@type boundary_type() ::
  :feel | :number | :integer | :float | :string | :char | :boolean
```

How a native function's argument or result is converted between FEEL and Elixir.

| boundary type | FEEL side                | Elixir side                    |
| ------------- | ------------------------ | ------------------------------ |
| `:feel`       | any value                | the value; a context is a plain map (D-135) |
| `:number`     | number                   | `t:Decimal.t/0`                |
| `:integer`    | integral number          | integer                        |
| `:float`      | number                   | float                          |
| `:string`     | string                   | binary                         |
| `:char`       | one-character string     | code point                     |
| `:boolean`    | boolean                  | boolean                        |

# `callable`

```elixir
@type callable() :: function() | {module(), atom()}
```

The Elixir function behind a native function.

# `kind`

```elixir
@type kind() :: :feel | :builtin | :native
```

# `param`

```elixir
@type param() :: {name :: String.t(), type :: Feel.Type.t()}
```

A parameter: its name and its FEEL type.

# `signature`

```elixir
@type signature() :: %{
  parameters: [{String.t(), Feel.Type.t(), boolean()}],
  variadic?: boolean(),
  returns: Feel.Type.t()
}
```

A signature of a built-in function as a value needs it: the parameters (name, type, and whether a call may
leave it out), whether the last parameter stands for all the arguments from its place on, and the type of the
result. A type that the function takes in more than one kind is `:any`.

# `t`

```elixir
@type t() :: %Feel.Value.Function{
  impl: term(),
  kind: kind(),
  name: String.t() | nil,
  origin: {:origin, term()} | nil,
  params: [param()],
  returns: Feel.Type.t()
}
```

# `arity`

```elixir
@spec arity(t()) :: non_neg_integer()
```

Returns the number of parameters of a function.

# `builtin`

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

Builds a built-in function as a value, from its name and its signatures (`Feel.Builtin.value/2` makes them from
the function). The parameters and the result of the first signature are those that `params/1` and `type/1` give.
`variants` are the signatures that the first ones were made of when they differ only in types (D-171): they are no
function type of the value, and say what a call of it does (`refinements/1`).

    iex> signature = %{parameters: [{"n", :number, false}], variadic?: false, returns: :number}
    iex> function = Feel.Value.Function.builtin("abs", [signature])
    iex> {function.kind, Feel.Value.Function.type(function)}
    {:builtin, {:function, [:number], :number}}

# `kind`

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

Returns the kind of a function.

# `name`

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

Returns the name of a function, or `nil` when it has none.

# `narrowings`

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

The built-in function restricted to each signature it made one of (`refinements/1`) that takes `arity` arguments, each as a
function of its own (D-197): `abs` for a number, for a days and time duration, and for a years and months duration. One
of them is what a declared function type is given when the function itself is not an instance of it
(`Feel.Type.Conversion`).

A restricted function has the one signature it was made of, with its parameter names and the types it declares for
this many arguments, and it **takes only those**: a call with another number of arguments, or with a value that is
not of the type of its parameter, is `null` with an error, as it is for a function of FEEL (`Feel.Runtime.Invoke`).
It is an instance of the function type of its signature and of no other, and has no refinement. None for a function that
is not a built-in, for one that was not made of several signatures, and for a variadic signature (a refinement is none
either).

    iex> {:ok, abs} = Feel.Builtin.value("abs")
    iex> [number | _] = Feel.Value.Function.narrowings(abs, 1)
    iex> {number.name, Feel.Value.Function.params(number), Feel.Value.Function.types(number)}
    {"abs", [{"n", :number}], [{:function, [:number], :number}]}
    iex> Feel.Value.Function.refinements(number)
    []
    iex> Feel.Value.Function.narrowings(abs, 2)
    []

# `native`

```elixir
@spec native(callable(), keyword()) :: t()
```

Builds a native function from an Elixir function.

## Options

  * `:name` - the function's name, for diagnostics.
  * `:params` - a list of `{name, boundary_type}` pairs, one per parameter.
    Defaults to no parameters.
  * `:returns` - the boundary type of the result. Defaults to `:feel`.

Raises `ArgumentError` for an unknown option or boundary type, or when
`callable` does not accept as many arguments as there are parameters.

    iex> function = Feel.Value.Function.native(&max/2, name: "maximum", params: [{"a", :integer}, {"b", :integer}], returns: :integer)
    iex> {Feel.Value.Function.arity(function), Feel.Value.Function.type(function)}
    {2, {:function, [:number, :number], :number}}

# `params`

```elixir
@spec params(t()) :: [param()]
```

Returns the parameters of a function with their FEEL types.

# `refinements`

```elixir
@spec refinements(t()) :: [Feel.Type.t()]
```

The function types that say what a call of a built-in function does for part of what it takes (D-171): those of the signatures
that were made one for a call by name (`Feel.Builtin.value/2`), each as it was. `abs` is a `function<Any> -> Any`, and a call of
it with a number gives a number, as `function<number> -> number` says; it says nothing of a call with a string. They are **not**
function types of the value: `abs instance of function<number> -> number` is `false`, as the specification has it, and a name
declared `function<number> -> number` is not given `abs` but the function of the signature for a number (`narrowings/2`, D-197). None for a function that was not made of several signatures, and for a
function of FEEL or of the host.

    iex> {:ok, abs} = Feel.Builtin.value("abs")
    iex> Feel.Value.Function.refinements(abs)
    [
      {:function, [:number], :number},
      {:function, [:days_and_time_duration], :days_and_time_duration},
      {:function, [:years_and_months_duration], :years_and_months_duration}
    ]
    iex> {:ok, before} = Feel.Builtin.value("before")
    iex> Feel.Value.Function.refinements(before)
    []

# `type`

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

Returns the FEEL function type of a function.

A parameter without a declared type is `:any`, and so is a result without one.

# `type`

```elixir
@spec type([Feel.Type.t()], Feel.Type.t()) :: Feel.Type.t()
```

Returns the descriptor of `function<T1, ..., Tn> -> R` for the given parameter
types and result type.

# `types`

```elixir
@spec types(t()) :: [Feel.Type.t()] | :open
```

All the function types of a function: one for a function of FEEL or of the host. For a built-in function, one
for each number of arguments that its signatures take, or `:open` if one of them is variadic and takes any number.

    iex> {:ok, length} = Feel.Builtin.value("string length")
    iex> Feel.Value.Function.types(length)
    [{:function, [:string], :number}]
    iex> {:ok, sum} = Feel.Builtin.value("sum")
    iex> Feel.Value.Function.types(sum)
    :open

# `types_at`

```elixir
@spec types_at(t(), non_neg_integer()) :: [Feel.Type.t()]
```

The function types that a function has for a number of arguments: `[type(function)]` for a function with as many
parameters, and none for another. A built-in function has one for each of its signatures that takes this many
arguments, the parameters of an optional parameter and of a variadic one being those of the arguments.

    iex> {:ok, date} = Feel.Builtin.value("date")
    iex> Feel.Value.Function.types_at(date, 3)
    [{:function, [:number, :number, :number], :date}]
    iex> Feel.Value.Function.types_at(date, 2)
    []

# `value?`

```elixir
@spec value?(term()) :: boolean()
```

Returns `true` for a `t:t/0`.

# `with_origin`

```elixir
@spec with_origin(t(), term()) :: t()
```

A function of FEEL whose body reports its diagnostics with `origin` (D-201).

The span of a diagnostic is a range of the text of the expression that made it. A function that one expression made and another
calls, as a host binds the business knowledge model that a decision calls, runs the text of the first inside the second, so
the diagnostics of its body have spans in a text that is not the one being run. With an origin they say whose text it is
(`Feel.Diagnostic`, `:origin`), and the host finds the span in it. The origin is whatever term the host likes.

A function that an expression writes has the origin of the run it is made in (the `:origin` option of `Feel.run/3`, `nil`
unless the host gave one, or the origin of the function whose body writes it), so a function passed to another and called
there, and one a function makes and gives back, report in their own text. This replaces that origin, for a function that
was made by a run that did not know whose text it is. A diagnostic of the call itself (the conversion of an argument, the
end of the call) is the caller's. A function that is not of FEEL runs no text, and is not changed.

    iex> body = fn env, [_a] ->
    ...>   {nil, Feel.Runtime.Env.report(env, Feel.Diagnostic.new(:invalid_property, "x", span: {0, 1}))}
    ...> end
    iex> function = Feel.Runtime.Invoke.closure([{"a", :any}], :any, body) |> Feel.Value.Function.with_origin(:member)
    iex> {nil, env} = Feel.Runtime.Invoke.call(Feel.Runtime.Env.new(), function, {:positional, [1]}, {0, 4})
    iex> Enum.map(Feel.Runtime.Env.diagnostics(env), & &1.origin)
    [:member]

---

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