# Overloaded functions and narrowing

[Narrowing](../narrowing.md) › overloaded functions. The names in the examples are those of [section 2](../narrowing.md#2-the-names-in-the-examples).

FEEL has no user-defined overloading: a name has one value. "Overloaded, with separate types" happens in three places, and this page goes through what narrowing does in each:

1. A **built-in with several signatures**: `before(point, point)`, `before(point, range)`, `before(range, point)` and `before(range, range)` are four signatures of one function; `abs(number)`, `abs(days and time duration)` and `abs(years and months duration)` are three of another.
2. A **built-in with one signature whose parameter takes several types**: `day of week` takes a date or a date and time, and gives a string.
3. A **function value whose type is a union of separate function types**: an `if` that picks one of two functions, or a built-in used as a value.

Three facts hold for all of them, and the rest of the page is their consequences:

- **The number of arguments, or their names, selects the signatures that fit. The *types* of the arguments rule out the ones they are certainly not for.**
- **The type of the result is the union of the results of the signatures that the arguments may be for,** with `Null` unless the function is never `null`. Many overloaded functions give the same result kind from every signature. `abs` has signatures with different result kinds, so a narrowed argument can make its result narrower.
- **A call that no signature takes is `null`** with an `argument_out_of_domain` warning, when the arguments are *certainly* not for any. Narrowing is what lets that check say "certain". The warning may mention inferred types; the executable call validates actual values and builds its runtime diagnostic from them, independently of the analysis tier.

## 1. A signature

A built-in is a `Feel.Builtin.Function` (D-56): a name, its signatures, the function that runs, and flags. A **signature** is a list of parameters and the types that the result can be:

| Part | Is |
| --- | --- |
| a parameter | a name, the types it accepts, and whether it is optional, variadic or quiet (`is defined`, D-100) |
| the result | a list of types: what the call can give, `null` aside |
| `extension: true` | the signature was added by an extension (D-81), [section 9](#9-overloads-of-an-extension) |
| `never_null?` (of the function) | the result is never `null` for an argument of a signature (`is`) |
| `check_arguments?` (of the function) | an argument no signature takes is `null` with an error before the function is called; `false` for `not`, whose own error for a wrong kind is another |

The names of the parameters are what puts a call with named arguments in order; the types describe the function and are what the analyzer types a call by. The function itself decides, when it runs, whether a value is one of them.

## 2. Which built-ins are overloaded

| Pattern | Functions | A call |
| --- | --- | --- |
| a point or a range, on either side (9) | `after`, `before`, `coincides`, `during`, `finished by`, `finishes`, `includes`, `started by`, `starts` | `before(1, [5..10])`, `includes(r, x)` |
| a list, or the values (10) | `all`, `any`, `max`, `mean`, `median`, `min`, `mode`, `product`, `stddev`, `sum` | `sum(xs)`, `sum(x, y)` |
| a key, or a list of keys (1) | `context put` | `context put(c, "b", 2)`, `context put(c, ["b", "c"], 2)` |
| a position, or a function that matches (1) | `list replace` | `list replace(xs, 1, 5)`, `list replace(xs, function(a, b) …, 5)` |
| a kind of number or of duration, with a result of that kind (1) | `abs` | `abs(x)`, `abs(dd)`, `abs(ym)` |
| arity only (13) | `ceiling`, `floor`, `round up`, `round down`, `round half up`, `round half down` (a scale is optional); `date`, `time`, `date and time`, `number`; `string join`, `sublist`, `substring` | `date("2024-01-31")`, `date(2024, 1, 31)` |
| one signature, a parameter of several types (5) | `day of week`, `day of year`, `month of year`, `week of year` (a date or a date and time); `years and months duration` | `day of week(d)` |

The first five rows have signatures that differ in the **types** of a parameter at one arity; the sixth in the number of parameters; the last has a single signature. In 34 of the 35 functions with several signatures, **every signature has the same result type**, and so does the single signature of the last row; which signature a call is for does not change its type. `abs` is the one that does: its three signatures take a number, a days and time duration and a years and months duration, and give that kind. So the registry says which result goes with which argument, and the type of a call (section 4) follows that.

## 3. Which signatures a call fits

**By position.** The signatures that accept this many arguments (`Feel.Builtin.Function.with_arity/2`): a variadic parameter takes any number from its place on, and an optional one may be left out. A call with a number of arguments that no signature takes is `null`, with an `arity_mismatch` warning when it is prepared and the error when it runs.

**By name.** The first signature that has a parameter of each name; a parameter the call leaves out is `null`. Where two signatures have the same names, **the order they are written in** decides: `before(point: x, range: r)` is the point-and-range signature, `before(range: r, point: x)` the range-and-point one. An argument given twice, and a name no signature has, are also an `arity_mismatch`. **Signatures that differ only in types are one choice** (`Feel.Builtin.Function.merge_variants/1`): the names do not say of what kind the arguments are, so `abs(n: dd)` is for the three signatures of `abs`, each of which has an `n`, and not for the first one only.

| Expression | Type | Warnings |
| --- | --- | --- |
| `date("2024-01-31")` | `date = 2024-01-31` | — |
| `date(2024, 1, 31)` | `date = 2024-01-31` | — |
| `date(x, 1, 31)` | `date \| Null` | — |
| `date()` | `Null` | `arity_mismatch` |
| `substring("abc", 2)` | `string = "bc"` | — |
| `substring("abc")` | `Null` | `arity_mismatch` |
| `sum()` | `Null` | `arity_mismatch` |
| `sum(n1: x)` | `number \| Null` | — |
| `abs(n: dd)` | `days and time duration in [PT0S..max] \| Null` | — |
| `abs(n: x)` | `number in [0..max] \| Null` | — |
| `before(point: x, range: r)` | `boolean \| Null` | — |
| `before(range: r, point: x)` | `boolean \| Null` | — |
| `before(range1: r, range2: r)` | `boolean \| Null` | — |

## 4. The type of the result

The result is `Feel.Analysis.Calls.result_type/3`: the union of the `result` of every signature **that the arguments may be for**, and `Null` unless the function is never `null`. A signature is left out when an argument is certainly not for it, which is the test of [section 5](#5-narrowed-arguments-and-the-certain-mismatch) applied to each signature alone, with one difference: a `null` that an argument may be does not take it for every signature. A `null` gives `null`, which the type has anyway, and the rest of the argument says which signature gives the other results (a parameter that takes `null` among its types does take it). If the arguments are certainly not for any signature the call is `null` with its error, and the type is the union of all of them, since the call is `Null` whatever the type says. A call with *constant* arguments of a pure function is evaluated (folded), and then has the exact type of its value.

Of the 35 functions with several signatures, 34 give one type from every signature, so the arguments change nothing for them. `abs` is the case this is for.

| Expression | Type | Warnings |
| --- | --- | --- |
| `abs(x)` | `number in [0..max] \| Null` | — |
| `abs(dd)` | `days and time duration in [PT0S..max] \| Null` | — |
| `abs(ym)` | `years and months duration in [P0M..max] \| Null` | — |
| `abs(dd + dd)` | `days and time duration in [PT0S..max] \| Null` | — |
| `abs(if b then dd else ym)` | `years and months duration in [P0M..max] \| days and time duration in [PT0S..max] \| Null` | — |
| `abs(v)` | `number in [0..max] \| years and months duration in [P0M..max] \| days and time duration in [PT0S..max] \| Null` | — |
| `abs(s)` | `Null` | — |
| `if v instance of number then abs(v) else 0` | `number in [0..max]` | — |
| `abs(-3)` | `number = 3` | — |
| `abs(@"PT1H")` | `days and time duration = PT1H` | — |
| `max(xs)` | `number \| Null` | — |
| `max(1, 2)` | `number = 2` | — |
| `sum(xs)` | `number \| Null` | — |
| `sum(1, 2, 3)` | `number = 6` | — |
| `before(x, r)` | `boolean \| Null` | — |
| `list replace(xs, 1, 5)` | `non-empty list<number \| Null> \| Null` | — |
| `context put(context: p, key: "zip", value: "x")` | `context<age: number \| Null, name: string \| Null, zip: string = "x"> \| Null` | — |
| `context put({a: 1}, "b", 2)` | `context<a: number = 1, b: number = 2>` | — |

- **`abs(dd)` is a days and time duration,** and `abs(ym)` a years and months duration; previously the type said all three kinds. The argument is the type of an expression, so `abs(dd + dd)` is as precise as the sum is.
- **An argument of two kinds gives both:** `abs(if b then dd else ym)` is either duration, not a number.
- **`abs(v)` gives all three.** `v` is `Any`, which every signature takes. Narrowing `v` to a number makes `abs(v)` a number, as the `if` shows: **a narrowed argument makes the result narrower.**
- **`abs(s)` is `Null`.** No signature takes a string, so the call is `null` whatever `s` is; the signatures alone would list all three kinds, since a `null` that is only possible does not make a certain mismatch (`abs("a")` does, and is `Null`), and the rule of `abs` (`Feel.Analysis.Images.Number`, D-161) says so. The rule also gives the numbers, from the sets of the argument: `abs(x)` is `number in [0..max]`, and a duration is `in [PT0S..max]`.
- **`max` and `list replace` follow the elements of the list** (D-151): a signature can say how its result is made from the types of the arguments (`result_from`), and `max(xs)` is the type of the elements of `xs`, and `list replace(xs, 1, 5)` a list of the elements and of the new item. This applies to 13 functions (below).

A constant gives an exact value: the function runs at `prepare` time, and the result is `number = 3`, `days and time duration = PT1H`, or the closed context that `context put` made.

### A result that follows the types of the arguments (D-151)

A function whose result depends on the *elements* of an argument, rather than on its kind, says so in its signature (`Feel.Builtin.Function.signature/3`, `result_from`), and `Calls.result_type/3` makes the type from the types of the arguments:

| Function | The result |
| --- | --- |
| `reverse`, `sort`, `distinct values`, `remove`, `sublist` | a list of the elements of the first argument |
| `append`, `insert before`, `list replace` | a list of the elements of the first argument and the items that are put in |
| `concatenate`, `union` | a list of the elements of every argument |
| `flatten` | a list of the elements, the lists among them taken apart |
| `max`, `min` of a list | one of the elements, or `null` |
| `max`, `min` of several arguments | one of the arguments, or `null` |

| Expression | Type | Warnings |
| --- | --- | --- |
| `reverse(xs)` | `list<number \| Null> \| Null` | — |
| `append(xs, s)` | `non-empty list<number \| string \| Null> \| Null` | — |
| `concatenate(xs, ys)` | `list<number \| string \| Null> \| Null` | — |
| `flatten([[1], [2, [3]]])` | `non-empty list<number in 1, 2, 3>` | — |
| `max(xs)` | `number \| Null` | — |
| `max(x, s)` | `Null` | — |
| `reverse(v)` | `list<Any \| Null> \| Null` | — |
| `append(xs, k)` | `non-empty list<number \| Null> \| Null` | — |
| `append([], k)` | `non-empty list<number>` | — |
| `max(append([], k))` | `number` | — |
| `max(append(xs, k))` | `number \| Null` | — |
| `max(k, 3)` | `number` | — |
| `if xs != [] then max(xs) else 0` | `number \| Null` | — |

A value that is not a list is treated as a list of itself, so `reverse(x)` is a list of numbers; a `null` argument is not a list and the call is `null`, so the elements of `x` (a number that may be `null`) are numbers, not `null`. An argument of type `Any` gives `Any` elements.

A result is a list that **has an element** (`non-empty list<...>`, D-152) when the call puts an item in (`append` with an item, `insert before`), or keeps the length of a list that has one (`reverse`, `sort`, `distinct values`, `list replace`, `concatenate` and `union` with one that has), and it is `null` only for a `null` that it is given when the function fails for nothing else (`append`, `concatenate`, `union`, `reverse`, `distinct values`, `flatten`; not those that take a position or a function). `max` and `min` of a list that has an element, of items of one ordered kind (numbers, strings, dates, durations) and no `null`, are not `null`: `max(append([], k))` is `number`, where `max(xs)` is `number | Null` for the `xs` of the tables, which may be empty and may hold a `null`.

## 5. Narrowed arguments and the certain mismatch

The type of an argument is used for one thing only, `Feel.Analysis.Calls.mismatch/3`: whether the arguments are ones that **none** of the signatures that fit takes (D-56). If so, the call is `null` with an `argument_out_of_domain` error, and the analyzer says so as a warning.

It must be **certain**:

- An argument is taken by a signature if **some alternative of its type** is compatible with some type of the parameter. A `Null`, an `Any` and a name of a model type are compatible with every parameter: **`null` excuses the pair.**
- The implicit conversions are allowed (section 10.3.2.9.4): a list of one element is its element where a value is wanted, and a value is a list of itself where a list is wanted; a date is a date and time where one is wanted (D-56). The length of a list is known for a list literal or a constant, and unknown otherwise.
- A parameter whose registry type is `any` takes every argument. When **several** signatures fit the call, it is a mismatch only if **every one** of them rejects an argument.
- A call that an **extension** may answer is not looked at (the extension takes the arguments it takes), and a function with `check_arguments?: false` is not checked.

That is why a declared name is not flagged and a narrowed one is: `s` is `string | Null`, and `null` excuses it, but the condition `s != null` (or `s = "a"`, or `s instance of string`) removes the `Null`, and the call becomes certain.

| Expression | Type | Warnings |
| --- | --- | --- |
| `abs(s)` | `Null` | — |
| `abs("a")` | `Null` | `argument_out_of_domain` |
| `if s != null then abs(s) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if s = "a" then abs(s) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if v instance of string then abs(v) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if v instance of number then abs(v) else 0` | `number in [0..max]` | — |
| `if v > 5 then string length(v) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if p.age > 5 then abs(p.name) else 0` | `number = 0 \| Null` | — |
| `{k: if b then 1 else "a", r: abs(k)}.r` | `number = 1 \| Null` | `null_possible` (an info) |
| `{k: if b then 1 else "a", r: if k instance of string then abs(k) else 0}.r` | `number = 0 \| Null` | `argument_out_of_domain` |

- **`abs(s)` is not flagged**: `s` may be `null`. `abs("a")` is flagged, because the constant `"a"` cannot be `null`.
- **A condition on a name** (`s != null`, `s = "a"`, `v instance of string`, `v > 5`) narrows the argument, and narrowing can make a call certain that was only possible before. `v > 5` makes `v` a number in the call, and `string length` takes no number.
- **A condition about *another* thing** does not help: `p.age > 5` narrows `p.age`, and `p.name` is still a string or `null`.
- **A union of alternatives** is excused if any of them is taken: `k` is a number or a string and `abs(k)` takes the number (and is a number, or `null` for the string). A local narrowed by `instance of` (`k instance of string`) has the string only, and the call is certain.
- **The result of a call that is certainly `null`** is `Null`. In `if … then abs(v) else 0` the `then` is `Null` and the `else` is `number = 0`, so the `if` is `number = 0 | Null`.

A narrowed argument also selects the signature for the result ([section 4](#4-the-type-of-the-result)): `if v instance of number then abs(v) else 0` types `abs(v)` as a number.

### Arguments that have to be of one kind (D-155)

A parameter says what one argument may be, and a point is a number, a string, a date, a time, a date and time or a duration, so each of the two points of `before(point1, point2)` is taken. They are also **compared with each other**, and a string and a number cannot be: the run gives `null` with the same error. A signature says which of its arguments have to agree (`same_kind`, `Feel.Builtin.Function`), and `Calls.mismatch/3` flags the arguments of a call that cannot agree, as it flags one that no parameter takes: `null`, with the warning.

| Signature | Arguments that agree |
| --- | --- |
| `before`, `after`, `coincides` of two points | the two points (a list of one element is the point it holds) |
| `min(list)`, `max(list)` | the elements of the list: those that are written out or a constant are each looked at, those of a list that is only typed are when it has an element |
| `min(c1, ..., cn)`, `max(c1, ..., cn)` | the arguments, as items; a list alone is the list of the items |

The kinds that an argument may be of are those of the members of its type that have an order. The same excuses hold as above: **`null`, `Any` and the name of a model type are of every kind**, so one argument of them agrees with everything. `before(s, x)` is not flagged for a `string | Null` and a `number | Null`, and is flagged for an `s` and an `x` that are not `null`.

| Expression | Type | Warnings |
| --- | --- | --- |
| `before(s, x)` | `Null` | — |
| `before("a", 2)` | `Null` | `argument_out_of_domain` |
| `if s != null and x != null then before(s, x) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if d != null and dt != null then coincides(d, dt) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if s != null then max(s, k) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `if s != null and s2 != null then max(s, s2) else 0` | `number = 0 \| string` | — |
| `max(k, 2)` | `number` | — |
| `max(["a", 1])` | `Null` | `argument_out_of_domain` |
| `if s != null then min([s, k]) else 0` | `number = 0 \| Null` | `argument_out_of_domain` |
| `max(true, false)` | `Null` | `argument_out_of_domain` |
| `max([])` | `Null` | — |
| `if s != null and r != null then before(s, r) else 0` | `boolean \| number = 0 \| Null` | `null_possible` (an info) |

- **A date is not a date and time**, and a years and months duration is not a days and time duration: `coincides(d, dt)` and `after(ym, dd)` are out of the domain, as `d = dt` is a mismatch. A time and a time with a zone are of one kind: whether they can be compared is decided at run time.
- **An item that has no order agrees with nothing**, a single one included: `max(true)` and `max([[1]])` are out of the domain, and the elements of a list that may be empty are not looked at (`max([])` is `null` with no error).
- **A range is not looked at.** The type of a range holds the range that has no endpoint (`(null..null)`), which can be related to a point of any kind, so `before(s, r)` is not a certain mismatch for a range of numbers. A range that is a constant is not looked at either.
- **The result of the call is `Null`** for a call that is certainly out of the domain, as for any other, and the type of a call that is not is unchanged: `max(s, s2)` of two strings that are not `null` is still a string.

## 6. A built-in as a value

A built-in is a value where its name stands alone and nothing in the scope or in a context has that name (`sort(xs, before)`, `{f: abs, r: f(-1)}.r`). A function type has a fixed number of parameters, so a built-in has a function type for **each number of arguments that a signature takes**, with the types of the parameters from that signature; a parameter that takes several types is `Any` (D-82). A function with a **variadic** parameter has no list of types, and is `Any`.

The value is **all of them**, and its type is an **overload** (D-171, `Feel.Type.Arrows`): `before` is a function of two points and a function of a point and a range, and so on, as `before instance of function<number, range<number>> -> boolean` says in the run (`true`: one of its signatures conforms). A reader that does not ask for the overload sees the function types as alternatives, which is the type a built-in had; the questions that compare function types (is it within, is it taken by a parameter, what is left of it after a test) are asked of all of them.

| Expression | Type | Warnings |
| --- | --- | --- |
| `abs` | `overload<function<Any \| Null> -> Any \| Null with function<number \| Null> -> number \| Null, function<days and time duration \| Null> -> days and time duration \| Null, function<years and months duration \| Null> -> years and months duration \| Null>` | — |
| `substring` | `overload<function<string \| Null, number \| Null> -> string \| Null, function<string \| Null, number \| Null, number \| Null> -> string \| Null>` | — |
| `before` | `overload<function<Any \| Null, Any \| Null> -> boolean \| Null, function<Any \| Null, range<Any \| Null> \| Null> -> boolean \| Null, function<range<Any \| Null> \| Null, Any \| Null> -> boolean \| Null, function<range<Any \| Null> \| Null, range<Any \| Null> \| Null> -> boolean \| Null>` | — |
| `string length` | `function<string \| Null> -> number \| Null` | — |
| `sum` | `Any` | — |
| `{f: abs, r: f(-1)}.r` | `number = 1` | — |
| `{f: abs, r: f(dd)}.r` | `days and time duration in [PT0S..max] \| Null` | — |
| `sort(xs, before)` | `list<number \| Null> \| Null` | — |
| `before instance of function<number, number> -> boolean` | `boolean = true` | — |
| `substring instance of function<string, number> -> string` | `boolean = true` | — |
| `substring instance of function<string> -> string` | `boolean` | — |
| `abs instance of function<Any> -> Any` | `boolean = true` | — |
| `abs instance of function<number> -> number` | `boolean` | — |

`substring` has two function types, one for each arity; `before` four, one for each signature, which all have two parameters and differ in their types. They are the **arrows** of the overload, and the value is each of them. `abs` has a signature for each kind, but the specification gives it one parameter that takes all three, and they differ only in types, so as a value they are one: `function<Any> -> Any`, and that is the one function type of `abs` (`abs instance of function<number> -> number` is `false` in the run, and a name or a parameter declared with that type is given the function of the signature that fits, a conversion, D-197). What a call of `abs` does with a number or a duration is **what the signatures it was made of say**: they are the **refinements** of the overload (after `with`), true of a call for the arguments in their parameters and silent about the others, and they are not function types of the value. `sum` is `Any`: its parameter is variadic.

A call of a key or a parameter that is **known** to be bound to a built-in (`{f: abs, r: f(-1)}.r`) is typed and folded as the call of the built-in: the type is `number = 1`, and the signature rules above apply, so `{f: abs, r: f(dd)}.r` is a days and time duration where `abs` as a value says `Any`. A callee that is an `if`, an item of a list or a key of a context literal says which functions it can be (D-193, section 8), and the call is the call of each of the built-in functions in it, with the rule of its family. A function that is not read (it came through a call, or an `if` nested more than four deep, or there are more than 4 of them) is typed by the overload, the arrows that take as many arguments and the refinements:

| Expression | Type | Warnings |
| --- | --- | --- |
| `(if b then abs else ceiling)(x)` | `number \| Null` | — |
| `(if b then abs else ceiling)(dd)` | `days and time duration in [PT0S..max] \| Null` | — |
| `(if b then abs else ceiling)(v)` | `number \| years and months duration in [P0M..max] \| days and time duration in [PT0S..max] \| Null` | — |
| `for x in xs return (if b then abs else ceiling)(x)` | `list<number \| Null> \| Null` | — |
| `(function(f: function<Any> -> Any, ys: list<number>) for x in ys return f(x))(abs, xs)` | `list<number in [0..max] \| Null> \| Null` | — |
| `(function(f: function<Any> -> Any, ys: list<number>) for x in ys return f(x))(if b then abs else ceiling, xs)` | `list<Any \| Null> \| Null` | — |
| `(if b then substring else before)("a", 1)` | `string = "a" \| Null` | — |
| `(if b then substring else before)(1)` | `Null` | `arity_mismatch` |
| `(function(f: function<Any, Any> -> boolean) f(1, 2))(before)` | `boolean = true` | — |
| `(function(f: function<number, range<number>> -> boolean, y: number) f(y, [1..5]))(before, k)` | `boolean` | — |
| `(function(f: function<number> -> number, y: number) f(y))(abs, k)` | `number \| Null` | — |
| `(function(f: function<number> -> number, y) f(y))(abs, dd)` | `Any \| Null` | — |
| `(function(f: function<days and time duration> -> days and time duration, y) f(y))(abs, dd)` | `days and time duration \| Null` | — |

The recorded arrows that take as many arguments and whose domains cover every argument bound the result; each applicable call refinement then narrows it. If no recorded arrow covers the call, its starting result is `Any | Null`, including at a recorded arity: an unrecorded same-arity signature may accept another domain. Arrows may have been omitted by a meet or a cap, so absence does not establish an arity error. A parameter of a function type **takes a built-in if one of its signatures is within the type**, as the run takes it, and the function is then known: the row with `before` and `f(1, 2)` is `before(1, 2)`, a constant. The last three rows are a parameter of `function<number> -> number` (or of the duration) that `abs` is not an instance of, and is not passed as it is: the run converts it to the function of the one signature of it that fits (D-197), which takes a number and is `null` for a duration. The type-only reader cannot establish this conversion's complete signature, so the duration call conservatively has `Any | Null`. Recursion links and literal functions carry separate callee evidence; ordinary function declarations do not. The rows with a loop show the use of a function that is passed on: the first is a function that is `abs` or `ceiling` called for each item; in the second `abs` is known to the parameter, and in the third the parameter is `abs` or `ceiling`, which the node of the argument says (D-193), and the call is each of them. `ceiling` is a function of a number and not of anything, so the run does not take it for `function<Any> -> Any` and the call is `null` then; the body that runs has `abs`, and the type, which has both, is wider than that.

## 7. Function values with separate types

A name, a key or an `if` can hold one of several functions, and then the callee has a **union of function types**. The call of a value (`Feel.Analysis.Invocations`, D-82) is typed from what the callee can be: each alternative gives a result, and the call is the union. (Where the node of the callee says which functions they are, the call is each of them called with the arguments, section 8; the types below are what a call has of the type of its callee, and the call has the values that are in both.)

| The alternative | Gives |
| --- | --- |
| `function<T1, …, Tn> -> R` | `R` when the arity matches and a recorded arrow covers every argument domain; otherwise start at `Any \| Null` and apply valid call refinements. An arrow declaration does not exclude other signatures, even at the same arity (D-171). |
| `Any`, or a name of a model type | `Any \| Null` |
| `null` | `null` |
| anything else (a number, a list, …) | `null`, and a `not_a_function` warning when the callee cannot be a function |

| Expression | Type | Warnings |
| --- | --- | --- |
| `(if b then function(a: number) a * 2 else function(a: string) a + "!")` | `function<number \| Null> -> number \| Null \| function<string \| Null> -> string \| Null` | — |
| `(if b then function(a: number) a * 2 else function(a: string) a + "!")(1)` | `number = 2 \| string \| Null` | — |
| `(if b then function(a: number) a * 2 else function(a: string) a + "!")("x")` | `number \| string = "x!" \| Null` | — |
| `(if b then function(a: number) a * 2 else function(a: string) a + "!")(v)` | `number \| string \| Null` | — |
| `(if b then function(a: number) a * 2 else function(a: string) a + "!")(1, 2)` | `Null` | `arity_mismatch` |
| `{g: if b then function(a: number) a * 2 else function(a: string) a + "!", r: g(1)}.r` | `number = 2 \| string \| Null` | — |
| `{g: if b then function(a: number) a * 2 else function(a: string) a + "!", r: if g instance of function<number> -> number then g(1) else null}.r` | `number = 2 \| Null` | — |
| `f(x)` | `number \| Null` | — |
| `f(x, y)` | `Any \| Null` | — |
| `v(1)` | `Any \| Null` | — |
| `x(1)` | `Null` | `not_a_function` |

- **The two functions are separate types, and a call is the union of both,** whatever the argument is. The number function given a string, and the string function given a number, are `null`; so the result has `Null`, and both `number` and `string`.
- **A condition separates them for the arguments it is about.** `g instance of function<number> -> number` removes `null`, and what a function that passes it is known to do is the test's type for a number (D-171, [function](function.md)): the string function is a function of a number only by returning `null`, so the call with `1` after it is `number | Null`, where the call of both without the test is `number | string | Null`. For an argument that the test does not take the function is what its own type says.
- **The arity is checked for a function that is known to have the arrows it has**: a function written in the expression and called at once or through a key (section 8), a callee whose node says which functions it is (D-193), and a built-in value whose registry signatures are known. A call of one with two arguments, when it takes one, is `null` with a warning. An overload type alone is not an exhaustive signature list. A function that is known only by its type (a name declared `function<number> -> number`, a parameter of that type, the alternatives of an `if`) has an arrow within the type and may have others, so the call is `Any | Null` with no warning (D-171).
- **A named call through a type alone is `Any | Null`**: function types have no parameter names. The names can select an unrecorded signature of another arity and result kind, even when fewer names are supplied than the recorded arity. Known function values keep their separate argument-binding analysis.

## 8. A function that is known

For guarded recursive parameters, specialization uses post-conversion bounds and discards operand shape, callee and origin evidence that conversion may invalidate. Recursive result projections can refine calls but cannot establish runtime signature membership; overlapping runtime signatures' possible results stay alternatives. See [function narrowing](function.md) and the [guarded-type contract](../type-system.md#guarded-recursive-types).

When the function is **known** (written in the expression and called at once, or through a key of a context, or said by the node of the callee, below), the call is not typed from the function's type but from its arguments (D-193): the body is **analyzed again**, with each parameter at the type of its argument and the facts that hold at the call. So the facts of the call narrow the body, and the body's own conditions narrow its parameters:

| Expression | Type | Warnings |
| --- | --- | --- |
| `(function(a: number) if a > 5 then a else 0)(7)` | `number = 7` | — |
| `(function(a) if a > 5 then a else 0)(3)` | `number = 0` | — |
| `(function(a) if a > 5 then a else 0)(v)` | `number in 0, (5..max]` | — |
| `(function(a: number) if a > 5 then a else 0)(x)` | `number in 0, (5..max]` | — |
| `{f: function(a: number) if a > 5 then a else 0, r: f(x)}.r` | `number in 0, (5..max]` | — |
| `(function(a: number) if x > 5 then a + x else a)(1)` | `number in 1, [6..max]` | — |

The first two are constants: the call is evaluated. The third has `a` as `Any` in the body, and the condition `a > 5` narrows it to a number above 5 in the `then`. The last two read an outer name `x`, whose fact holds in the body too. How deep a call is specialized is limited to three calls (`@specializations`, D-193). Reanalysis also has an aggregate 64 KiB source budget and reads at most four callee alternatives. Once a budget is spent the call uses conservative type information. Full analysis has a separate bounded kind-only signature pass; the two passes keep their own reanalysis budgets (D-219). The shared worker additionally contains the complete public preparation call; reducing repeated analysis within that boundary is still [open performance work](../todo.md#performance).

From its first analysis, a literal function keeps its observable runtime arrow separate from its more precise call result. The arrow is captured by kind-only analysis of its written environment, so neither condition facts, call specialization nor optimization can strengthen it (D-219). `function(a: number) 1` has a nullable `number` result signature and a call refinement returning `1`; a non-null body result does not make `instance of function<number> -> number!` true. Calling it still benefits from that precise body result. Non-null parameters remain non-null in public projection; a domain that cannot be projected safely makes the whole function `Any`.

**A function that the body writes** is analyzed for the arguments too, but that reanalysis changes its call summary, not its inspectable signature. The runtime signature of `function(a) h` comes from kind-only analysis in its written environment (`h` is `Any`), and that is what `instance of` and a declared type ask about. So a call that gives a function gives the overload of the arrow as it was written, which is what the algebra sees, with the arrow of this analysis as a refinement, which types a call and nothing else (D-171):

| Expression | Type | Warnings |
| --- | --- | --- |
| `(function(h) function(a) h)(x)` | `overload<function<Any \| Null> -> Any \| Null with function<Any \| Null> -> number \| Null>` | — |
| `(function(h) function(a) h)(x) instance of function<Any> -> Any` | `boolean = true` | — |
| `(function(h) function(a) h)(x) instance of function<Any> -> number` | `boolean` | — |
| `(function(h) function(a) h)(x)(1)` | `number \| Null` | — |
| `(function(x) function(y) x + y)(1)(2)` | `number = 3` | — |
| `(function(x) function(y) x + y)(1) instance of function<number> -> number` | `boolean` | — |

The third row was `boolean = true` to the analysis and `false` to the run; the refinement is why the fourth and the fifth are still exact.

### A callee that says which function it is (D-193)

A function reaches a call through a key of a context literal, an item of a list literal or a branch of an `if` as a type. A literal FEEL closure keeps its runtime arrow and call refinements inside the container; nested exclusions and function domains are not rewritten by normalization (D-152, D-171). Known-callee metadata can keep additional detail beyond that type, so it is read (`Feel.Analysis.Callees`): an `if` is its two branches, `list[index]` of a list literal is its items, `context.key` of a context literal is the entry, and a name of a key of a context is its value. A name that is bound to a list or a context keeps the functions in the shapes of its structure (D-193), so `fs[1]`, `c.g` and a chain of them (`c.d.g`, `fs[1].g`) are read from the name, and so is a parameter that a call gives such a node and the iterator of a `for`, `some` or `every` over a list of them. A function literal in it is analyzed again for the arguments, in the place it was written, as one written at the call is, and a built-in function is typed as its call by name would be, with the rule of its family. The call is any of them, and `null` too where the callee or an argument may not fit, and it has the values that are also in what the type of the callee gives (a condition about the callee, such as `g instance of function<number> -> number`, narrows its type).

| Expression | Type | Warnings |
| --- | --- | --- |
| `{g: function(a) a * 2}.g(x)` | `number \| Null` | — |
| `[abs, floor][1](x)` | `number \| Null` | — |
| `(if b then function(a) a * 2 else function(a) a + 1)(3)` | `number in 4, 6` | — |
| `(if b then abs else ceiling)(age)` | `number in [0..150] \| Null` | — |
| `{f: if b then abs else ceiling, r: f(age)}.r` | `number in [0..150] \| Null` | — |
| `(if b then function(a, c) a else function(a, c) c)(1)` | `Null` | `arity_mismatch` |
| `[function(a) a, 5][1](3)` | `Any \| Null` | — |
| `{fs: [abs, floor], r: fs[1](x)}.r` | `number \| Null` | — |
| `{c: {g: function(a) a * 2}, r: c.g(x)}.r` | `number \| Null` | — |
| `{fs: [{g: function(a) a}, {g: function(a) a + 1}], r: fs[1].g(3)}.r` | `number in 3, 4` | — |
| `for f in [abs, floor] return f(x)` | `list<number \| Null>` | — |
| `(function(f: function<Any> -> Any, y) f(y))(if b then abs else ceiling, x)` | `Any \| Null` | — |
| `(function(f) f(3))(if b then function(a) a else function(a) a + 1)` | `number in 3, 4` | — |
| `(function(f: list<Any>) f(3))(if b then abs else ceiling)` | `Null` | `not_a_function` |
| `{fs: (function() [abs])(), r: fs[1](x)}.r` | `Any \| Null` | — |
| `{g: function(a) function(c) a + c}.g(1)(2)` | `number = 3` | — |
| `(if b then function(a) function(c) a + c else function(a) function(c) a - c)(1)(2)` | `number in -1, 3` | — |
| `{fs: [abs, floor]}.fs[1](x)` | `number \| Null` | — |
| `[[abs, floor]][1][1](x)` | `number \| Null` | — |
| `(function(fs) fs[1](x))([abs, floor])` | `number \| Null` | — |
| `(function(c) c.g(3))({g: function(a) a * 2, h: 5})` | `number = 6` | — |
| `{g: function(a) {h: function(c) a + c}}.g(1).h(2)` | `Any \| Null` | — |

The function in the rows that are `Any | Null` is not read: an item that is a number, a list or a context that a call gives (a name has the shape of a list that is written, D-193), and a call that gives a context of functions. A parameter is the function that it is given when the run does not convert the argument: a declared type that is an `Any` takes it as it is. A declared arrow can convert a built-in to a restricted signature (D-197), so callee identity is kept only when the argument type conforms without conversion. A declared list can wrap it as a list of one; that parameter is typed by its type. There are at most 4 alternatives, and 64 KiB of function source is analyzed again for them in one analysis (the body of each, again for the body of a function in it); a call past either is typed by the type of its callee.

**A call that gives a function (D-193).** The body of a function that is analyzed again for a call says which functions it gives when its value is a function written in it, an `if` of them, a name that has them, or a call that gives them (`Callees.alternatives/2` of the node of the body); the node of the call keeps them (`callees` in its metadata, answered to a call that repeats this one with the body's type, D-193), and a call of that call reads them as it reads any callee: `g(1)(2)` for a `g` that a key, a list item, an `if`, a name or a parameter holds. The function is written in the place of the body, with its parameters at the types of the arguments of the first call, so the second call is typed from both. A body that may give a value that is not a function (`function(a) if a > 1 then abs else 1`) says nothing, and a call that is `null` for sure gives none. The type of a function that a call of a refinement gives is the arrow as it was written with the arrow of the refinement as a refinement, at every depth (`function(a) function(c) function(d) a + c + d` called with `null` and `5` is not a function of `null`, which the run's function is not an instance of).

**A literal that is selected from more than once, and a parameter that is given a list or a context (D-193).** A list or a context literal is a container as the shape of a name is: `{fs: [abs, floor]}.fs[1]`, `[[abs, floor]][1][1]` and `(if b then {g: f} else {g: h}).g` read the items and the entries of the literals that the node can be, four deep. A parameter that a call gives a list or a context literal, or a name that has the structure of one, with functions in it, has the structure of the argument (D-193), when the run does not convert the argument (it is within the declared type), and so `fs[1]`, `c.g` and the iterator of a loop over it are read as they are for a name that is bound to the value, and a comparison of it with a list of another length is decided (`(function(p) p = [abs, abs])([abs])` is `boolean = false`). A parameter that is given a list or a context with no function in it has no structure, as it had none.

## 9. Overloads of an extension

An extension adds signatures to a function DMN 1.6 has (`overloads/0`, D-81): the `:camunda` extension gives six functions another signature (D-81). They are marked `extension: true`. The function of DMN 1.6 **runs first**; an overload gets its turn only if that reports an error, and may answer `:next` for arguments that are not for it. A call that an extension may answer is marked in its metadata and:

- is **not checked** for a certain mismatch (the extension takes the arguments it takes);
- is **folded with the extensions** of the expression, as a call of DMN 1.6 is (D-45);
- has the **types** of the extension's signature in its result, not the registry's.

| Expression | Without `:camunda` | With `:camunda` |
| --- | --- | --- |
| `number("1,000.5", ",")` | `Null` | `number = 1000.5` |
| `string join(["a", "b"], ", ", "[", "]")` | `Null` | `string = "[a, b]"` |
| `substring("abc", 2, 10)` | `Null` | `string = "bc"` |
| `get value({a: {b: 1}}, ["a", "b"])` | `Null` | `number = 1` |
| `context put({a: 1}, ["b", "c"], 2)` | `Null` | `context<a: number = 1, b: context<c: number = 2>>` |

Without the extension the first two calls are an `arity_mismatch` (the signatures do not exist) and the third gives `null` at run time; with it the overload answers. The overloads: `number(from, grouping separator)`, `date and time(date and time, timezone)`, `string join` with a prefix and a suffix, `get value` with a list of keys, `context put` that makes the path, and `substring` with a length past the end.

## 10. What is not done

- **No generics.** A result that follows the elements of an argument (`max(xs)`) is a rule of the registry (D-151), not an arrow; a function type with a type variable is not in the language.
- **A refinement has the result of its signature, not the rule of its family** (D-161), where the callee is not read: a built-in function that reaches the call through a call that gives it, an `if` nested more than four deep or more than 4 functions has the overload of `abs` and `ceiling`, and its call for an `age` of `[0..150]` is `number | Null` (a refinement says `number`) and not `number in [0..150] | Null`, which `abs(age)` by name is. A callee whose node says which functions it is has the rule (section 8), and so does a name, a parameter and an iterator that hold them (D-193).
- **Returned containers do not preserve every callable summary** (D-152, D-193): `{fs: (function() [abs])(), r: fs[1](dd)}.r` is `Any | Null`. Literal FEEL closures keep their signature/call distinction inside a container; that does not make all built-in family rules or known-callee metadata survive a returned container. Nested closures fall back to their written parameters and `Any | Null`; equal arrow types in another entry or argument do not establish closure identity. A top-level closure uses a unique unspecialized written arrow when one can be paired, otherwise it keeps the broader unspecialized function alternatives.
- **No agreement check between the endpoints of a range and a point** (`before(s, r)`, `includes(r, s)`, `meets(r, r2)`): a range type has the range with no endpoint (section 5), so the kinds of its endpoints are not the kinds it can be related to. [todo.md](../todo.md) describes what it would take.
- **No signature has a different result from another signature of its function**: in all 34 they share one. If one ever does, the result type will be the union, as it is now for `abs`.

See also: [function](function.md), [Any](any.md), [null](null.md), [range](range.md); `Feel.Analysis.Calls`, `Feel.Analysis.Invocations` and `Feel.Builtin.Function`. The [decision record](../decisions.md) explains the call boundary (D-82), signature contracts (D-171), and propagated summaries (D-193).
