# `Feel.Analysis.Inference`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/analysis/inference.ex#L1)

The static result types of operations, for `Feel.Analysis.Analyzer`.

A type here is a `Feel.Type.Union`, a list of alternatives with `:null` among them
when the value may be `null` (D-39). Given the types of the operands, each function
returns `{:ok, type}` or `{:mismatch, type}`; the second means that the operation is a
type mismatch, so `null` with an error, whenever no operand is `null`.

The mirror of this module at run time is `Feel.Runtime`; a test runs both on the same
operands and checks that the value of the one is among the alternatives of the other.

## How a union is typed

Each pair of alternatives, one of each operand, is typed alone, and the result is the
union of what the pairs give. `[:number, :null] + [:number]` is the pairs
`number + number`, which is `number | null`, and `null + number`, which is `null`.
A pair that is a type mismatch gives `null`.

The result is `{:mismatch, type}` when every pair without a `null` is a type mismatch, and
there is one: the operation is an error unless an operand is `null`. The type is the same
union as for `{:ok, type}`; it is `[:null]` except for `=` and `!=`, which are `false` and
`true` for a `null` operand. A union with some pairs that are mismatches is a possible
mismatch, which is not reported.

## Value sets

An alternative can be a set of values (D-39): `{:number, set}`, `{:string, strings}`, or
`{:boolean, true}` or `{:boolean, false}`; the bare `:number`, `:string` and `:boolean` are every
value of the kind. For numbers the operation works on the set (`Feel.Analysis.Intervals`): the
result is the set of what the operation can give, and it is `null` for lack of range only where
the intervals say it can be, so `[0..150] + 1` is `[1..151]` and never `null` in itself, and a
divisor that does not have zero in it is never zero. A comparison of two sets that settles it is
the boolean it settles to: `[0..150] < 200` is `{:boolean, true}`. The strings of sets are
joined for `+` and compared for `=` and `!=`; a boolean that is known decides `and` and `or`.

This module works out the kind of a result and whether it can be `null`, and puts the sets into the alternatives. What
the sets of the operands come to, and whether they decide a comparison, is asked of
`Feel.Analysis.Refinement.Operations` (D-133), which calls the modules named below; this module calls none of them.

A date and the two kinds of duration have sets too (D-109), which are sets of the numbers that stand
for the values (`Feel.Type.Image`: the days of a date, the months or the seconds of a duration), so the
same intervals work on them. The seconds of `PT1H + PT2H` are `3600 + 7200`, and `PT1H * 2` is the seconds
times 2: the result is the set of the durations, and `null` where the numbers can overflow or divide by
zero, as for a number. The months of a years and months duration do not overflow at run time, so a sum of
them that the numbers say could pass the range is any years and months duration, and not `null`. Two dates,
or two durations of one kind, are compared by their numbers: `@"PT1H" < @"P1D"` is `{:boolean, true}`. What
the numbers do not carry is worked out from the sets of both: a years and months duration times a number,
which is rounded (D-54) and so is not a product of images, has the months rounded as the run does
(`Feel.Analysis.MonthScaling`, D-109), and a date plus or minus a duration, which is not a sum of images
either, since the day of the month is clamped (`2024-01-31 + P1M`), has the dates of `Feel.Analysis.DateArithmetic`
(D-109). A time and a date and time have sets of the second they fall in, on three lines, for the clock and for the
instant, since their order depends on a zone (`Feel.Type.Moment`, D-109): a comparison of two of them is decided by
`Feel.Analysis.MomentTests`, a time or a date and time plus or minus a duration has the second that the sets come
to (`Feel.Analysis.MomentArithmetic`, D-109), and so does the difference of two of them (D-109).

## The types it takes

The types are plain (`Feel.IR.plain_type/1`, D-152, D-152): a union whose members are the kinds whole. A kind less some of
it and the empty list (`Feel.Type.Union.positive/1` makes the list of nothing of it) are for what passes a type on and for
the narrowing, and a function here that is given one takes it for a kind it does not know.

## What a pair gives

`null` as an operand gives `null`, except for `=` and `!=` (Table 50), which never give
`null` because of an operand, and for `and` and `or`, where a `false` or a `true` decides.
An alternative of the type `:any` or a named type is not known, so the result is
`[:any, :null]` or `[:boolean, :null]`, unless the other operand decides it alone: a kind with no order for `<` and
`>` (D-196), a kind with no operation for `+ - * / **` (D-203); or unless the kinds say more: an operation on a kind that
has an operation and one that is not known is the union of what the pairs give (D-203). The rest is below.

### Arithmetic

A number operation can give `null`: it overflows past decimal128, it can be too small to write, `/` divides
by zero, and `**` has no value for a zero to a negative power or a negative number to a power that is not
whole. Whether it does is decided by the sets of the operands (see above), so on any number it can, and on
numbers that stay away from the ends of the range and from zero it cannot: `0 ** x` for an `x` in `[5..10]`
is the number 0, and `x ** 2` is `[25..100]` (D-161).

| Operator | Operands                                                | Result                       |
| -------- | ------------------------------------------------------- | ---------------------------- |
| `+`      | two strings                                             | `string`                     |
| `+` `-`  | two years and months durations                          | `years and months duration` (never `null` in itself) |
| `+` `-`  | two days and time durations                             | `days and time duration`, `null` |
| `+ - * / **` | two numbers                                         | `number`, `null`             |
| `*`      | a days and time duration and a number, in either order  | `days and time duration`, `null` |
| `/`      | a days and time duration and a number                   | `days and time duration`, `null` |
| `/`      | two durations of one kind                               | `number`, `null`             |
| `*` `/`  | a years and months duration and a number                | `years and months duration`, a set of the months rounded as the run does, and `null` (D-109) |
| `+` `-`  | a date and a duration                                   | `date`, a set of the days the run can give, and `null` where the year can leave the range (D-109) |
| `+` `-`  | a date and time and a duration                          | `date and time`, a set of the seconds the run can give, and `null` where the clock can leave the calendar (D-109) |
| `+` `-`  | a time and a days and time duration                     | `time`, a set of the seconds of the day round the day (D-109) |
| `-`      | two dates                                               | `days and time duration` (a set, from the days between) |
| `-`      | two dates and times, two times, a date and a date and time | `days and time duration`, a set of the seconds between them to a second either way, and `null` (one has a zone, one has not, D-109) |
| negation | a number or a duration                                  | the same type                |

Every other pair of kinds is a mismatch, since the run does the operations of the temporal tables
(D-54) and gives a type mismatch for the rest.

An operand of a kind that an operator has no operation for is a mismatch against any other operand, `Any` and a named
type included (D-203): `true + v` is `null` for every `v`, an error for one that is not `null`. A boolean, a list, a
range, a context, a function and a term that is no value of FEEL have no operation at all; a string has `+` and no
other; a date, a time and a date and time have `+` and `-`; a duration has all but `**`; only a number has `**`. So
`true + v`, `[1] - v`, `"a" - v` and `d * v` are `Null`. A kind that has an operation against one that is not known (`Any`,
a named type) is a value of the kinds that its pairs give with a kind of the unknown one, or `null` (D-203): the unknown
one is a value of every kind, a pair that no table names is `null`, and a `null` is `null`. `x + v` is `number | Null` (a
number is added to a number only), `"a" + v` is `string | Null`, `d + v` is `date | Null`, `x * v` is `number | years and
months duration | days and time duration | Null`, and `v + w` is the seven kinds that have a `+`, and `Null`. The pairs are of
the kinds whole, so the set of values of the known operand does not cut the result (it is as narrow as the kinds say, and
it is a function of the operator and the kinds, which is kept). `-v` is a number, a years and months duration, a days
and time duration, or `Null` (D-207): the kinds that a negation has. One unknown value read twice (`v - v`, D-170, see
`arithmetic_of_one/3`) is the kinds of the pair of a kind with itself, and `Null`. A union is `null` only if every pair
of its alternatives is.

Negation cannot overflow, and a result that is not `null` is assumed for a number that
is well formed: at most 34 digits and in the exponent range. A bound number that is not
is `null` at run time and the type does not say so.

### Comparison

`=` and `!=` give `boolean` and never `null` for operands of one primitive kind, or
when an operand is `null`. Lists, contexts and ranges can hold elements that cannot be
compared, so they give `boolean` and `null`. `<`, `<=`, `>` and `>=` give `null` for a
`null` operand, and `boolean` for two numbers, two strings, two dates, two times, two dates
and times, or two durations of one kind (a time or a date and time with a zone that the time
zone database does not have would be `null`, and a value of FEEL has none). Operands of one
kind that has no order, such as two booleans, are a mismatch (`null`, with a `type_mismatch`
warning, D-193), for `<=` and `>=` as for `<` and `>`: `true <= true` is `null` (the domain reading of D-196, D-196).
Operands of different kinds are a mismatch, a date and a date and time, or the two kinds of duration,
included (notes 02, A-38; D-54).

An operand that has no order (a boolean, a list, a range, a context, a function, a term that is no value of FEEL) is
a mismatch against any other operand, `Any` and a named type included (D-196): `true < v` and `true <= v` are `null`
for every `v`, an error for one that is not `null`.

### Logic

`and` and `or` give `boolean` for two booleans, and `boolean` and `null` when an operand
can be `null` or is not a boolean but the other is, since a `false` decides a conjunction
and a `true` a disjunction whatever the other operand is. An operand that is never a boolean (`null`, a number, a
string) leaves one outcome out: `b and null` is `false` or `null` and never `true`, `b or null` is `true` or `null`
and never `false` (Tables 51 and 52, `Feel.Runtime.Logic`; D-170). When no operand can be a boolean
the type is `null`, and not a mismatch: the run gives a plain `null` for an operand that is not a boolean (D-103).

### Conditional

`if` is the one construct here that cannot be a mismatch, and `conditional/3` returns a
type and not a result. The `then` branch is taken for a condition that is `true` and the
`else` branch for anything else, `null` and a value that is not a boolean included
(Table 50), so the condition adds nothing to the type. The type is the union of the types
of the branches that can be taken (`reachable/1`): a condition that is `true` for sure
leaves the `then` branch, and one that cannot be `true` leaves the `else` branch.

# `comparison_operator`

```elixir
@type comparison_operator() :: Feel.IR.comparison_operator()
```

A comparison operator, as in `t:Feel.IR.comparison_operator/0`.

# `grains`

```elixir
@type grains() :: {integer() | nil, integer() | nil}
```

How fine the numbers of each operand are (`Feel.Analysis.Grain`, D-161): a multiple of 10^-d, or `nil`. It lets a product or a
quotient of numbers that are not near zero be one that is never too small to write (D-161).

# `operator`

```elixir
@type operator() :: Feel.IR.arithmetic_operator()
```

An arithmetic operator, as in `t:Feel.IR.arithmetic_operator/0`.

# `result`

```elixir
@type result() :: {:ok, Feel.Type.Union.t()} | {:mismatch, Feel.Type.Union.t()}
```

What a function of this module returns.

# `shape`

```elixir
@type shape() :: %{type: Feel.Type.Union.t(), structure: structure()}
```

What the analyzer knows of a node besides its type (D-193): its `type`, and its `structure`. A list literal, a constant list,
and a context bound to one have a structure: the shapes of the elements of a list of up to 32, or only the number of them;
the shapes of the entries of a closed context of up to 32. Anything else has `:none`.

# `structure`

```elixir
@type structure() ::
  :none
  | {:list, [shape()]}
  | {:size, non_neg_integer()}
  | {:context, %{optional(Feel.Name.t()) =&gt; shape()}}
```

# `arithmetic`

```elixir
@spec arithmetic(operator(), Feel.Type.Union.t(), Feel.Type.Union.t(), grains()) ::
  result()
```

Returns the type of `left <operator> right`.

    iex> Feel.Analysis.Inference.arithmetic(:add, [:string], [:string])
    {:ok, [:string]}
    iex> Feel.Analysis.Inference.arithmetic(:add, [:number], [:number])
    {:ok, [:number, :null]}
    iex> Feel.Analysis.Inference.arithmetic(:add, [:number, :null], [:null])
    {:ok, [:null]}
    iex> Feel.Analysis.Inference.arithmetic(:add, [:number], [:string])
    {:mismatch, [:null]}
    iex> Feel.Analysis.Inference.arithmetic(:add, [:number], [:number, :string])
    {:ok, [:number, :null]}

# `arithmetic_of_one`

```elixir
@spec arithmetic_of_one(operator(), Feel.Type.Union.t(), grains()) :: result()
```

Returns the type of `x <operator> x` for one value `x` that is read twice (D-170), given the type of `x`: the two operands
are not two values of the type, so the pairs of two different alternatives are not there, and `x - x` is `0` (a date and
a time give the zero duration), `x / x` is `1` unless `x` is `0`, `x * x` is not below `0`, and `x + x` is `2x`. An
alternative that is not a number, or a duration or a date for `-`, or a duration for `/`, is typed as it is for two
values. `null` is an alternative of the result for an `x` that is, as it is for two. An `x` of an unknown type (`Any`, a named
type) is one value of one kind, the same at both ends (D-207): the union, over the kinds that the operator has an operation
for, of the type for that kind, and `null`. `x - x` is `0` of a number, a years and months duration or a days and time
duration, `x + x` is a number, a string or a duration, `x * x`, `x / x` and `x ** x` are a number; the pairs of two kinds
(a date and a duration) that `x - y` has are not there.

    iex> Feel.Analysis.Inference.arithmetic_of_one(:subtract, [:number]) |> elem(1) |> Feel.Type.Union.format()
    "number = 0"
    iex> Feel.Analysis.Inference.arithmetic_of_one(:subtract, [:number, :null]) |> elem(1) |> Feel.Type.Union.format()
    "number = 0 | Null"
    iex> Feel.Analysis.Inference.arithmetic_of_one(:divide, [:number]) |> elem(1) |> Feel.Type.Union.format()
    "number = 1 | Null"
    iex> Feel.Analysis.Inference.arithmetic_of_one(:multiply, [:string])
    {:mismatch, [:null]}
    iex> Feel.Analysis.Inference.arithmetic_of_one(:subtract, [:any]) |> elem(1) |> Feel.Type.Union.format()
    "number = 0 | years and months duration = P0M | days and time duration = PT0S | Null"
    iex> Feel.Analysis.Inference.arithmetic_of_one(:multiply, [:any]) |> elem(1) |> Feel.Type.Union.format()
    "number in [0..max] | Null"

# `between`

```elixir
@spec between(
  Feel.Type.Union.t(),
  Feel.Type.Union.t(),
  Feel.Type.Union.t(),
  {Feel.Type.Union.t(), Feel.Type.Union.t()}
  | {Feel.Type.Union.t(), Feel.Type.Union.t(),
     {Feel.Type.Union.t(), Feel.Type.Union.t()}}
  | nil
) :: result()
```

Returns the type of `value between low and high` (D-69), which is `value >= low and value <= high`: the
types of the two comparisons, and then of the three-valued `and`. So it is `true` when the sets of the
value and of the bounds put it between them, `false` when they put it outside, `null` when an operand is
`null` (but `false` if the other bound already puts it outside), and a type mismatch only when both
comparisons are one: `1 between "a" and "b"`.

`after_lower` is `{value, high}`, the types of the value and of the upper bound when they were typed
knowing that the lower comparison is `true` (D-194), as the right operand of an `and` is
(`conjunction_after/2`): `x between 5 and 3` for an `x` in `[0..10]` is then `false`, since for an `x` of
`[5..10]` the upper comparison is. Without it the two comparisons are typed apart.

`{value, high, {value_otherwise, high_otherwise}}` adds the types of the value and of the upper bound where the lower
comparison is not true. The upper comparison is typed for each case of the lower one, as the range of an `in` is
(`conjunction_cases/3`, D-194): `n between 0 and 10` for an `n` in `[0..10]` that may be `null` is `true` where the lower
comparison is, and `null` where it is not, the `n` being `null` there. An upper bound that reads a name that the lower
comparison narrows has another type there (`n between 0 and n`).

    iex> Feel.Analysis.Inference.between([:number], [:number], [:number])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.between([:number, :null], [:number], [:number])
    {:ok, [:boolean, :null]}
    iex> Feel.Analysis.Inference.between([:number], [:string], [:string])
    {:mismatch, [:null]}
    iex> Feel.Analysis.Inference.between([:number], [:string], [:number])
    {:ok, [{:boolean, false}, :null]}
    iex> Feel.Analysis.Inference.between([:null], [:number], [:number])
    {:ok, [:null]}

# `branches`

```elixir
@spec branches({boolean(), boolean()}, Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  Feel.Type.Union.t()
```

Returns the type of `if … then then_type else else_type` for the branches that can be taken, `{then?, else?}`: the
union of their types. At least one is taken. `reachable/1` has them for the type of the condition; the analyzer also
leaves out a branch whose facts leave a name no value (D-152).

    iex> Feel.Analysis.Inference.branches({true, true}, [:number], [:string])
    [:number, :string]
    iex> Feel.Analysis.Inference.branches({false, true}, [:number], [:string])
    [:string]

# `compare`

```elixir
@spec compare(comparison_operator(), Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  result()
```

Returns the type of `left <operator> right` for a comparison.

    iex> Feel.Analysis.Inference.compare(:lt, [:number], [:number])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.compare(:eq, [:number, :null], [:number])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.compare(:lt, [:number, :null], [:number])
    {:ok, [:boolean, :null]}
    iex> Feel.Analysis.Inference.compare(:lt, [:number], [:null])
    {:ok, [:null]}
    iex> Feel.Analysis.Inference.compare(:eq, [:number], [:string])
    {:mismatch, [:null]}
    iex> Feel.Analysis.Inference.compare(:lt, [{:string, ["a", "b"]}], [{:string, ["c"]}])
    {:ok, [{:boolean, true}]}
    iex> Feel.Analysis.Inference.compare(:lt, [{:string, ["a", "b"]}], [{:string, ["b"]}])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.compare(:gte, [{:string, ["a", "b"]}], [{:string, ["c"]}])
    {:ok, [{:boolean, false}]}
    iex> Feel.Analysis.Inference.compare(:eq, [{:list, [:any, :null]}], [{:list, [:null]}])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.compare(:eq, [{:list, [:number, :null]}, :null], [{:list, [:null]}])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.compare(:neq, [{:list, []}], [{:list, []}])
    {:ok, [{:boolean, false}]}
    iex> Feel.Analysis.Inference.compare(:eq, [{:list, [:number]}], [{:list, [:string]}])
    {:ok, [:boolean, :null]}

# `conditional`

```elixir
@spec conditional(Feel.Type.Union.t(), Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  Feel.Type.Union.t()
```

Returns the type of `if condition then then_type else else_type`: the union of the types of
the branches that `reachable/1` says can be taken.

    iex> Feel.Analysis.Inference.conditional([:boolean], [:number], [:string])
    [:number, :string]
    iex> Feel.Analysis.Inference.conditional([:boolean, :null], [:number], [:null])
    [:number, :null]
    iex> Feel.Analysis.Inference.conditional([{:boolean, true}], [:number], [:string])
    [:number]
    iex> Feel.Analysis.Inference.conditional([:null], [:number], [:string])
    [:string]

# `conjunction_after`

```elixir
@spec conjunction_after(Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  Feel.Type.Union.t()
```

Returns the type of `left and right` when `right` was typed knowing that `left` is `true` (D-194): the
names that `left` says something about have, in `right`, the types that it leaves them
(`Feel.Analysis.Narrowing`).

Both operands are evaluated (D-53), but the result is `right` only where `left` is `true`. Where `left` is
`false` it is `false`. Where `left` is `null`, or not a boolean, it is `false` or `null`, and nothing more
is known, since `right` was typed for the case that `left` is `true` and is evaluated with the names as
they are: `x > 5 and x = null` for an `x` that is `null` has `x > 5` `null` and `x = null` `true`, though
`x = null` is `false` for the `x` that `x > 5` leaves. So the result is taken for each case of `left`.

    iex> Feel.Analysis.Inference.conjunction_after([:boolean], [{:boolean, false}])
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.conjunction_after([:boolean, :null], [{:boolean, false}])
    [{:boolean, false}, :null]
    iex> Feel.Analysis.Inference.conjunction_after([{:boolean, true}], [:boolean, :null])
    [:boolean, :null]
    iex> Feel.Analysis.Inference.conjunction_after([{:boolean, false}], [:boolean, :null])
    [{:boolean, false}]

# `conjunction_cases`

```elixir
@spec conjunction_cases(Feel.Type.Union.t(), Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  Feel.Type.Union.t()
```

Returns the type of `left and right` when `right` was typed twice (D-194): as `right_when_true` for the case that
`left` is `true`, and as `right_otherwise` for the case that it is not. Where `conjunction_after/2` can only say
that a `left` that is `null` gives `false` or `null`, this knows what the right is for the names as they are
then: `x >= 1 and x <= 10` for an `x` of `[1..3]` that may be `null` is `true`, and `null` where `x` is.

It is for a right operand that is cheap to type again, the upper comparison of a range.

    iex> Feel.Analysis.Inference.conjunction_cases([{:boolean, true}, :null], [{:boolean, true}], [:null])
    [{:boolean, true}, :null]
    iex> Feel.Analysis.Inference.conjunction_cases([{:boolean, true}, :null], [{:boolean, true}], [:boolean, :null])
    [:boolean, :null]
    iex> Feel.Analysis.Inference.conjunction_cases([:boolean], [{:boolean, false}], [:null])
    [{:boolean, false}]

# `disjunction_after`

```elixir
@spec disjunction_after(Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  Feel.Type.Union.t()
```

Returns the type of `left or right` when `right` was typed knowing that `left` is not `true` (D-194): the
names that `left` says something about when it is `false` or `null`, or not a boolean, have, in `right`,
the types that it leaves them (`Feel.Analysis.Narrowing`).

It is the other way round from `conjunction_after/2`. Both operands are evaluated (D-53), but `right` matters
only where `left` is not `true`, since `true or right` is `true` whatever `right` is. And everything but
`true` is what the facts of an `else` say, `null` and the values that are not booleans included, so the
narrowed `right` holds for every case of `left` that it can matter for, and the result is `left or right`
for each of them.

    iex> Feel.Analysis.Inference.disjunction_after([{:boolean, true}], [:null])
    [{:boolean, true}]
    iex> Feel.Analysis.Inference.disjunction_after([:boolean], [{:boolean, false}])
    [:boolean]
    iex> Feel.Analysis.Inference.disjunction_after([{:boolean, false}], [:boolean])
    [:boolean]
    iex> Feel.Analysis.Inference.disjunction_after([:boolean], [:boolean])
    [:boolean]
    iex> Feel.Analysis.Inference.disjunction_after([{:boolean, false}, :null], [{:boolean, false}])
    [{:boolean, false}, :null]

# `instance_of`

```elixir
@spec instance_of(Feel.Type.Union.t(), Feel.Type.t()) :: Feel.Type.Union.t()
```

Returns the type of `value instance of type` (Table 62), given the type of the value and the
type, resolved.

It is a boolean and never `null`, whatever the value is: `null instance of number` is `false`,
where `null < 1` is `null`. It is `true` for sure when the value is never `null` and the union is
within the type, and `false` for sure when the value is `null` and the type is not `Null`, or
the type is `Null` and the value is never `null`, or when what the value can be, `null` aside, has no value
in common with the type (`Feel.Type.Union.intersect/2`, D-194): a `number` is not a `string`. A `list<string>`
is still `boolean` against `list<number>`, for the empty list is both. Anything else is any boolean.

    iex> Feel.Analysis.Inference.instance_of([:number, :null], :string)
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.instance_of([{:list, [:string]}], {:list, :number})
    [:boolean]
    iex> Feel.Analysis.Inference.instance_of([{:list, [:string]}], :number)
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.instance_of([:number], :number)
    [{:boolean, true}]
    iex> Feel.Analysis.Inference.instance_of([:number, :null], :number)
    [:boolean]
    iex> Feel.Analysis.Inference.instance_of([:null], :number)
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.instance_of([:null], :null)
    [{:boolean, true}]
    iex> Feel.Analysis.Inference.instance_of([:number], :null)
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.instance_of([:number], :any)
    [{:boolean, true}]
    iex> Feel.Analysis.Inference.instance_of([:null, :any], :any)
    [:boolean]

# `list_contains`

```elixir
@spec list_contains(Feel.Type.Union.t(), boolean()) :: Feel.Type.Union.t()
```

Returns the type of a test of `in` that is an expression, `x in [1, 2, 3]` (D-61), given the type of the expression and
whether it reads the input `?`.

A list is `list contains`: the input is in it when it equals an element, or lies within an element that is a range,
and an element that it cannot be compared with is not equal to it. That is `true` or `false` for every input, `null`
included (`Feel.Runtime.UnaryTests.member/3`). A `null` is the value `null`, which the input equals or does not; but a
test that reads `?` and is `null` is the answer itself, so it is `null`. Anything else is equality with the input, or a
range's containment, which are `null` for an input they cannot be compared with. The type is the exact one of the node
(`Feel.IR.type/1`), so that the empty list, which a plain reader takes for a list of `null`, is told from the rest: nothing
is in it, so the answer is `false` for every input.

    iex> Feel.Analysis.Inference.list_contains([{:list, []}], false)
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.list_contains([{:list, []}, :null], false)
    [:boolean]
    iex> Feel.Analysis.Inference.list_contains([{:list, [:number]}], false)
    [:boolean]
    iex> Feel.Analysis.Inference.list_contains([{:list, [:number]}, :null], false)
    [:boolean]
    iex> Feel.Analysis.Inference.list_contains([{:list, [:number]}, :null], true)
    [:boolean, :null]
    iex> Feel.Analysis.Inference.list_contains([{:list, [:number]}, :number], false)
    [:boolean, :null]
    iex> Feel.Analysis.Inference.list_contains([:any, :null], false)
    [:boolean, :null]

# `logic`

```elixir
@spec logic(:and | :or, Feel.Type.Union.t(), Feel.Type.Union.t()) :: result()
```

Returns the type of `left and right` or `left or right`.

    iex> Feel.Analysis.Inference.logic(:and, [:boolean], [:boolean])
    {:ok, [:boolean]}
    iex> Feel.Analysis.Inference.logic(:or, [:boolean, :null], [:boolean])
    {:ok, [:boolean, :null]}
    iex> Feel.Analysis.Inference.logic(:and, [:null], [:null])
    {:ok, [:null]}
    iex> Feel.Analysis.Inference.logic(:and, [:number], [:string])
    {:ok, [:null]}
    iex> Feel.Analysis.Inference.logic(:and, [:boolean], [:null])
    {:ok, [{:boolean, false}, :null]}
    iex> Feel.Analysis.Inference.logic(:or, [:boolean], [:number])
    {:ok, [{:boolean, true}, :null]}
    iex> Feel.Analysis.Inference.logic(:and, [{:boolean, true}], [:null])
    {:ok, [:null]}
    iex> Feel.Analysis.Inference.logic(:or, [:any], [:null])
    {:ok, [{:boolean, true}, :null]}
    iex> Feel.Analysis.Inference.logic(:and, [:any], [{:boolean, false}])
    {:ok, [{:boolean, false}]}
    iex> Feel.Analysis.Inference.logic(:and, [:any], [:boolean])
    {:ok, [:boolean, :null]}

# `membership`

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

Returns the type of `subject in tests` (D-61), given the types of the tests.

The value is the three-valued OR of the tests, taken alternative by alternative (D-194): it can be `true`
when a test can be, `false` when every test can be `false`, and `null` when no test is `true` and a test is
not `false` either. So a test that is `false` or `null` makes the whole `false` or `null`, never `true`,
and `Null` is among the alternatives only when a test can be `null`.

    iex> Feel.Analysis.Inference.membership([[{:boolean, false}, :null]])
    [{:boolean, false}, :null]
    iex> Feel.Analysis.Inference.membership([[:boolean], [:null]])
    [{:boolean, true}, :null]
    iex> Feel.Analysis.Inference.membership([[{:boolean, true}], [:boolean, :null]])
    [{:boolean, true}]
    iex> Feel.Analysis.Inference.membership([[{:boolean, false}], [{:boolean, false}]])
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.membership([[:boolean], [{:boolean, false}]])
    [:boolean]
    iex> Feel.Analysis.Inference.membership([[:boolean], [:boolean, :null]])
    [:boolean, :null]
    iex> Feel.Analysis.Inference.membership([[:null], [:null]])
    [:null]

# `negate`

```elixir
@spec negate(Feel.Type.Union.t()) :: result()
```

Returns the type of `-operand`. An operand of an unknown type (`Any`, a named type) is a number, a years and months
duration, a days and time duration, or `null` (D-207): the kinds that a negation has.

    iex> Feel.Analysis.Inference.negate([:number])
    {:ok, [:number]}
    iex> Feel.Analysis.Inference.negate([:any])
    {:ok, [:number, :years_and_months_duration, :days_and_time_duration, :null]}
    iex> Feel.Analysis.Inference.negate([:number, :null])
    {:ok, [:number, :null]}
    iex> Feel.Analysis.Inference.negate([:string])
    {:mismatch, [:null]}

# `null_bounds`

```elixir
@spec null_bounds(
  operator(),
  Feel.Type.Union.t(),
  Feel.Type.Union.t(),
  grains(),
  boolean()
) ::
  :near_zero | :too_large | :range | nil
```

Which bounds on the operands of `left <operator> right` would rule `null` out (D-161), when neither
operand can be `null`: `:near_zero` for operands at least 0.001 away from zero, `:too_large` for
operands of at most 1E+30, `:range` where the result needs both, and `nil` where it is not `null`,
where it is certain (two single numbers, which is a warning of its own, D-161), or where these bounds
do not rule it out. It is what lets the analyzer say, in an info, that tighter allowed values would
rule `null` out, and the numbers say so: the operands are restricted as the hint says, and the
result has no `null` left.

    iex> alias Feel.Type.NumberSet
    iex> to_150 = [{:number, NumberSet.interval(Decimal.new(0), Decimal.new(150))}]
    iex> to_max = [{:number, NumberSet.interval(Decimal.new(0), NumberSet.max_value())}]
    iex> two = [{:number, NumberSet.point(Decimal.new(2))}]
    iex> fraction = [{:number, NumberSet.point(Decimal.new("1.19"))}]
    iex> Feel.Analysis.Inference.null_bounds(:divide, to_150, two)
    :near_zero
    iex> Feel.Analysis.Inference.null_bounds(:multiply, to_max, two)
    :too_large
    iex> Feel.Analysis.Inference.null_bounds(:multiply, to_max, fraction)
    :range
    iex> Feel.Analysis.Inference.null_bounds(:multiply, to_150, two)
    nil
    iex> Feel.Analysis.Inference.null_bounds(:divide, to_150 ++ [:null], two)
    nil

# `range`

```elixir
@spec range(Feel.Type.Union.t(), Feel.Type.Union.t()) ::
  {:ok, Feel.Type.Union.t()} | {:mismatch, Feel.Type.Union.t()}
```

Returns the type of the range literal `[from..to]` (D-61), given the types of its endpoints.

A range is made of two endpoints of one kind that has an order, and a `null` endpoint is no
endpoint, so the range is `range<T>` for the union `T` of what the endpoints can be, and never `null`,
when every pair of kinds that they can be makes one. It is `{:mismatch, [:null]}` when no pair does
(`[1.."a"]`), and it has `Null` among its alternatives when only some do. A kind that is not known
(`Any`) can be any.

    iex> Feel.Analysis.Inference.range([:number], [:number])
    {:ok, [{:range, [:number]}]}
    iex> Feel.Analysis.Inference.range([:number], [:null])
    {:ok, [{:range, [:number]}]}
    iex> Feel.Analysis.Inference.range([:number], [:string])
    {:mismatch, [:null]}
    iex> Feel.Analysis.Inference.range([:number, :string], [:number])
    {:ok, [{:range, [:number, :string]}, :null]}
    iex> Feel.Analysis.Inference.range([:any], [:number])
    {:ok, [{:range, [:any]}, :null]}

# `reachable`

```elixir
@spec reachable(Feel.Type.Union.t()) :: {boolean(), boolean()}
```

Returns which branches of `if condition then … else …` a run can take, as
`{then?, else?}`.

The `then` branch is taken when the condition is `true`, so only if its type has an
alternative that can be `true`. The `else` branch is taken for any other value, so unless
the condition is `true` for sure. One of them is always taken.

    iex> Feel.Analysis.Inference.reachable([:boolean, :null])
    {true, true}
    iex> Feel.Analysis.Inference.reachable([{:boolean, true}])
    {true, false}
    iex> Feel.Analysis.Inference.reachable([{:boolean, false}])
    {false, true}
    iex> Feel.Analysis.Inference.reachable([:number, :null])
    {false, true}

# `shapes_equal`

```elixir
@spec shapes_equal(:eq | :neq, shape(), shape()) :: Feel.Type.Union.t()
```

Returns the type of `left = right` and `left != right` for two nodes whose structure is known (D-193, D-193, D-161).

The structure is what the type does not have: a FEEL list type has no length, so a list in the entry of a context or in a
list is `list<number>`. Here it is followed to every depth:

  * lists of **other lengths** are unequal for sure, whatever the elements (`Feel.Runtime.Compare`), and the result is
    never `null`;
  * lists of one length, and contexts with the same keys, are equal by the conjunction of the equality of what they hold, each
    pair of elements and each entry by the same rule (so `{a: [x]} = {a: [x, y]}` is `false`);
  * contexts that cannot have the same keys are unequal for sure;
  * anything else is by the types, which is what `compare/3` makes of them.

The conjunction is that of the run (`all_equal/1`): the result can be `false` if a pair can, `true` if all can, and `null`
if a pair can and no pair is `false` for sure.

    iex> leaf = fn type -> %{type: type, structure: :none} end
    iex> list = fn items -> %{type: [{:list, [:number, :null]}], structure: {:list, items}} end
    iex> Feel.Analysis.Inference.shapes_equal(:eq, list.([leaf.([:number])]), list.([leaf.([:number]), leaf.([:number])]))
    [{:boolean, false}]
    iex> Feel.Analysis.Inference.shapes_equal(:eq, list.([leaf.([:number])]), list.([leaf.([:number])]))
    [:boolean]
    iex> Feel.Analysis.Inference.shapes_equal(:eq, list.([leaf.([:number])]), list.([leaf.([:string])]))
    [:null]
    iex> Feel.Analysis.Inference.shapes_equal(:neq, list.([]), list.([]))
    [{:boolean, false}]
    iex> inner = fn count -> list.(List.duplicate(leaf.([:number]), count)) end
    iex> Feel.Analysis.Inference.shapes_equal(:eq, list.([inner.(1)]), list.([inner.(2)]))
    [{:boolean, false}]

---

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