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

The kinds of subject some facts are about, and the subjects that are paths from each name (D-194, D-154).

A read of a name is narrowed by the facts about the paths from it (`p` after `p.age > 5`, D-194), and the analyzer asks
of the facts around it whether there are any, and which. Looking through all of them for that is a look at the facts of
every other name, for each read: in `v1.a > 1 and v2.a > 1 and …`, with a name for each term, the n-th read looked at
n - 1 facts that were not about it, and a chain was quadratic in cheap steps. So the subjects that are paths from a
name are kept by the name, and a read looks up its own. They are kept with the facts, and made from those of a chain
the way the facts are (`Feel.Analysis.Narrowing.combine/5`), so a read costs the facts about the name it reads.

A *source* is what a path starts at: a name (`"p"` for `{:path, "p", ["age"]}`), or a local told by the depth of the
context that binds it and its name (`{2, "a"}` for `{:local_path, 2, "a", ["age"]}`, D-193). The paths of an element
of a filter are not kept: nothing reads them as the paths of a source.

# `source`

```elixir
@type source() :: Feel.Name.t() | {pos_integer(), Feel.Name.t()}
```

What a path starts at: a name, or a local as `{depth of the context that binds it, name}`.

# `t`

```elixir
@type t() :: %Feel.Analysis.Narrowing.Kinds{
  kinds: MapSet.t(atom()),
  paths: %{
    optional(source()) =&gt; %{optional(Feel.Analysis.Narrowing.subject()) =&gt; true}
  }
}
```

The kinds of subject (`:name` for a variable, and the tag of the others, as `t:Feel.Analysis.Narrowing.subject/0` has
them), and the subjects that are paths from each source, as the keys of a map.

# `new`

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

The kinds of no subject at all.

    iex> Feel.Analysis.Narrowing.Kinds.to_list(Feel.Analysis.Narrowing.Kinds.new())
    []

# `of_subjects`

```elixir
@spec of_subjects(Enumerable.t()) :: t()
```

The kinds of the subjects.

    iex> kinds = Feel.Analysis.Narrowing.Kinds.of_subjects(["x", {:path, "p", ["age"]}, {:path, "p", ["size"]}])
    iex> Feel.Analysis.Narrowing.Kinds.to_list(kinds)
    [:name, :path]
    iex> kinds |> Feel.Analysis.Narrowing.Kinds.paths_from("p") |> Map.keys() |> Enum.sort()
    [{:path, "p", ["age"]}, {:path, "p", ["size"]}]
    iex> Feel.Analysis.Narrowing.Kinds.paths_from(kinds, "x")
    nil

# `only?`

```elixir
@spec only?(t(), [atom()]) :: boolean()
```

Whether the subjects are all of the kinds given (and so of none other).

    iex> kinds = Feel.Analysis.Narrowing.Kinds.of_subjects(["x", {:path, "p", ["age"]}])
    iex> Feel.Analysis.Narrowing.Kinds.only?(kinds, [:name, :path])
    true
    iex> Feel.Analysis.Narrowing.Kinds.only?(kinds, [:name])
    false

# `paths_from`

```elixir
@spec paths_from(t(), source()) ::
  %{optional(Feel.Analysis.Narrowing.subject()) =&gt; true} | nil
```

The subjects that are paths from a source, as the keys of a map, or `nil` if there are none.

# `put`

```elixir
@spec put(t(), Feel.Analysis.Narrowing.subject()) :: t()
```

The kinds with the kind of one more subject, and the subject if it is a path from a source.

# `to_list`

```elixir
@spec to_list(t()) :: [atom()]
```

The kinds of subject, as a sorted list.

# `union`

```elixir
@spec union(t(), t()) :: t()
```

The kinds and the paths of both: those of the facts that two sets of facts make when both hold. The side with fewer
sources is the one that is walked, so a chain of `n` terms that is joined to one more is not `n` steps for it.

    iex> alias Feel.Analysis.Narrowing.Kinds
    iex> a = Kinds.of_subjects([{:path, "p", ["age"]}])
    iex> b = Kinds.of_subjects(["x", {:path, "p", ["size"]}])
    iex> union = Kinds.union(a, b)
    iex> {Kinds.to_list(union), union |> Kinds.paths_from("p") |> map_size()}
    {[:name, :path], 2}

---

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