# `Feel.Type.Except`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/type/except.ex#L1)

The members of a union that are a list, a range, a context or a function **less** some others (D-152).

A kind that has no set of values for its parts cannot be cut as a number can. The lists that are not lists of numbers are not
a list of anything: `list<Any | Null>` less `list<number | Null>`. So the union has a member for it, `{:except, base, negations}`:
the values of `base` (a list, a range, a context or a function type) that are in none of the `negations` (types of the same kind).
It is the symbolic form of `base ∧ ¬n1 ∧ ¬n2 ...`, as a negative literal is in a decision diagram of types, and it is what makes the
four kinds closed under difference: `Feel.Type.Union.difference/2` does not have to keep a kind whole when it is partly taken out.

A member is **in normal form** (`build/2`) when

  * `base` is a plain type of the kind (never an `:except`) and the negations are plain types of the same kind;
  * each negation shares values with `base` (one that does not takes nothing out) and none is within another (the smaller
    takes nothing the larger does not);
  * it has values: `empty?/2` has not shown that the negations cover `base`; and it has at least one negation, since without
    one it is `base`.

The kind is the **family** of the member: `:list`, `:range`, `:context` (the open and the closed) or `:function`.

## Deciding whether it has values

`empty?/2` is the one question that the kinds differ in, and it is answered **soundly**: `true` only when the negations are
shown to cover the base. A member that is empty and not shown to be is kept, which is a type that holds more than it has to.

  * A **list** of `P` less lists of `N1`, `N2`, ...: a list has as many elements as it likes, so it can avoid each `Ni` with an
    element of its own. It has values unless one `Ni` holds all of `P` (and a list that holds one is a list of that kind, however
    long): the lists are empty only if `P` is within some `Ni`.
  * A **range** of `P` less ranges of `N1`, ...: a range has two ends, and each end can avoid some of the `Ni`. It has values if
    the `Ni` can be shared between the two ends so that each end has a value of `P` that none of its share holds.

  * A **context** of `base` less contexts `N1`, `N2`, ...: a negation lists some entries with a type for each, so it is a box,
    the product of those types, and the contexts it holds are the points in it. The question is whether the box of the base is
    covered by the boxes of the negations, which is a search that splits the types of one entry at a time along the types
    that the negations list for it, and goes on to the next entry in each part (`context_plans/3`). An entry that the base
    does not list is there or not if the base is open (`:absent` is one of the parts) and is not there if it is closed. A
    negation that is closed is left by a context with an entry it does not list, which an open base may have and a closed
    base has if it lists one the negation does not; a negation that lists an entry a closed base cannot have holds nothing;
    both are dropped. The order of the entries is not a part of a type, so it plays no part. At most 4,096 parts are
    visited, and 256 for one entry; past that the context is taken to have values.
  * A **function** has no procedure: its member is kept unless one negation holds the base (an arrow is within another
    when its parameters are wider and its result narrower, and a function that avoids several arrows can be made of a case
    for each, so the single test is the whole of it for plain arrows).

# `atom_type`

```elixir
@type atom_type() :: Feel.Type.Union.member()
```

A type of one family: a plain one, or an `:except`.

# `family`

```elixir
@type family() :: :list | :range | :context | :function
```

The kinds that have members of this form.

# `plan`

```elixir
@type plan() :: %{
  optional(String.t() | :extra) =&gt; Feel.Type.Union.t() | :absent | true
}
```

A way to be a context of the base that is in none of the negations: for each entry that matters, the part of its type that the
context has a value in, or `:absent` if it has none; and `:extra => true` if it also has an entry that no negation lists (what
leaves a closed negation when the base is open). The other entries of the base can have any value.

# `base`

```elixir
@spec base(atom_type()) :: atom_type()
```

The plain type under a member: the member itself if it is plain.

# `build`

```elixir
@spec build(atom_type(), [atom_type()]) :: [atom_type()]
```

The member that is the values of `base` less those of the `negations`, as a list of the one member, or none if it has no value
(`[]`), or `base` alone if nothing is taken out.

    iex> list = {:list, [:any, :null]}
    iex> Feel.Type.Except.build(list, [{:list, [:number, :null]}])
    [{:except, {:list, [:any, :null]}, [{:list, [:number, :null]}]}]
    iex> Feel.Type.Except.build({:list, [:number]}, [{:list, [:number, :null]}])
    []
    iex> Feel.Type.Except.build({:closed_context, %{"a" => [:number]}}, [{:closed_context, %{"b" => [:string]}}])
    [{:closed_context, %{"a" => [:number]}}]

# `context_plans`

```elixir
@spec context_plans(atom_type(), [atom_type()], pos_integer()) ::
  {:found, [plan()]} | :covered | :bound
```

The contexts of `base` that are in none of the `negations`, as at most `limit` plans, or `:covered` if there are none, or `:bound`
if the search was cut before it knew (`:covered` is a proof, and so is each plan).

    iex> base = {:closed_context, %{"a" => [:number], "b" => [:string]}}
    iex> half = {:closed_context, %{"a" => [{:number, Feel.Type.NumberSet.interval(Decimal.new(0), Decimal.new(5))}], "b" => [:string]}}
    iex> {:found, [plan]} = Feel.Type.Except.context_plans(base, [half], 1)
    iex> Map.keys(plan)
    ["a"]
    iex> Feel.Type.Except.context_plans(base, [base], 1)
    :covered

# `empty?`

```elixir
@spec empty?(atom_type(), [atom_type()]) :: boolean()
```

Whether the values of `base` less those of the `negations` are shown to be none (see the module).

    iex> list = {:list, [:number, :null]}
    iex> Feel.Type.Except.empty?(list, [{:list, [:number, :null, :string]}])
    true
    iex> Feel.Type.Except.empty?({:list, [:number, :string]}, [{:list, [:number]}, {:list, [:string]}])
    false

# `extend`

```elixir
@spec extend(atom_type(), [atom_type()]) :: [atom_type()]
```

The member less some more plain types of its kind: `build/2` of the negations it has and the new ones. The negations of a
member are in normal form already, none within another, so only the new ones are compared with them and with each other,
which for one more is as many looks as there are negations, and not as many as there are pairs of them: a dispatch on a name
takes one more type out at each test, and there are as many tests as a source has room for.

    iex> list = {:list, [:any, :null]}
    iex> [member] = Feel.Type.Except.extend(list, [{:list, [:number, :null]}])
    iex> Feel.Type.Except.extend(member, [{:list, [:string, :null]}]) == Feel.Type.Except.build(list, [{:list, [:number, :null]}, {:list, [:string, :null]}])
    true

# `family`

```elixir
@spec family(Feel.Type.Union.member()) :: family() | nil
```

Whether a member is a type with parts of a family that `:except` is for.

# `meet`

```elixir
@spec meet(atom_type(), atom_type()) :: [atom_type()]
```

The values in both of two members of one family (an intersection of two types is one type): the bases meet, and the negations
of both are taken out.

    iex> a = {:list, [:number, :string, :null]}
    iex> b = {:list, [:number, :null]}
    iex> Feel.Type.Except.meet(a, {:except, {:list, [:any, :null]}, [b]})
    [{:except, {:list, [:number, :string, :null]}, [{:list, [:number, :null]}]}]

# `minus`

```elixir
@spec minus(atom_type(), atom_type()) :: [atom_type()]
```

The values of the first that are not in the second: at most one member for the base of the second taken out, and one for each of
its negations (a value that is in the second's base and in a negation of it is not in the second).

    iex> list = {:list, [:any, :null]}
    iex> number = {:list, [:number, :null]}
    iex> Feel.Type.Except.minus(list, number)
    [{:except, {:list, [:any, :null]}, [{:list, [:number, :null]}]}]
    iex> Feel.Type.Except.minus(number, list)
    []

# `negations`

```elixir
@spec negations(atom_type()) :: [atom_type()]
```

The types taken out of a member.

# `range_plans`

```elixir
@spec range_plans(atom_type(), [atom_type()], pos_integer()) ::
  {:found, [{Feel.Type.Union.t(), Feel.Type.Union.t()}]} | :covered | :bound
```

The ways for a range of `base` to be in none of the `negations`, as at most `limit` pairs of types, one for each end: the values
that the start can have and the values that the end can have. `:covered` if there are none, `:bound` if the negations are
more than the search shares between two ends.

    iex> base = {:range, [:number, :string]}
    iex> Feel.Type.Except.range_plans(base, [{:range, [:number]}, {:range, [:string]}], 2)
    {:found, [{[:number], [:string]}, {[:string], [:number]}]}
    iex> Feel.Type.Except.range_plans(base, [{:range, [:number, :string]}], 1)
    :covered

# `subset?`

```elixir
@spec subset?(atom_type(), atom_type()) :: boolean()
```

Whether every value of the first is in the second, as far as it is shown (`minus/2` has no member left).

# `top`

```elixir
@spec top(family()) :: Feel.Type.Union.member()
```

The type of every value of the family.

---

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