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.
Summary
Types
What a path starts at: a name, or a local as {depth of the context that binds it, name}.
The kinds of subject (:name for a variable, and the tag of the others, as Feel.Analysis.Narrowing.subject/0 has
them), and the subjects that are paths from each source, as the keys of a map.
Functions
The kinds of no subject at all.
The kinds of the subjects.
Whether the subjects are all of the kinds given (and so of none other).
The subjects that are paths from a source, as the keys of a map, or nil if there are none.
The kinds with the kind of one more subject, and the subject if it is a path from a source.
The kinds of subject, as a sorted list.
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.
Types
@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}.
@type t() :: %Feel.Analysis.Narrowing.Kinds{ kinds: MapSet.t(atom()), paths: %{ optional(source()) => %{optional(Feel.Analysis.Narrowing.subject()) => true} } }
The kinds of subject (:name for a variable, and the tag of the others, as Feel.Analysis.Narrowing.subject/0 has
them), and the subjects that are paths from each source, as the keys of a map.
Functions
@spec new() :: t()
The kinds of no subject at all.
iex> Feel.Analysis.Narrowing.Kinds.to_list(Feel.Analysis.Narrowing.Kinds.new())
[]
@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
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
@spec paths_from(t(), source()) :: %{optional(Feel.Analysis.Narrowing.subject()) => true} | nil
The subjects that are paths from a source, as the keys of a map, or nil if there are none.
@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.
The kinds of subject, as a sorted list.
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}