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

The type of `e.name` from the type of `e` (D-65).

`select/3` is the static counterpart of `Feel.Runtime.Path.select/2`: it takes the union that is the
type of the source and the key, and gives the union that the selection can be and whether the run can
report an error for it. A test checks the two against each other on many values.

The union of the source is taken one alternative at a time, and the result is the union of what each
gives:

| The alternative            | The selection                                                            | An error |
| -------------------------- | ------------------------------------------------------------------------ | -------- |
| `context<..., key: T, ...>`| `T`, whatever `T` is: a context type lists entries that are there (D-23) | never    |
| an open context without the key | `Any \| Null` (a context type allows more entries than it lists) | maybe |
| a closed context without the key | `Null`: it has the entries it lists and no others                    | always   |
| `list<E>`                  | `list<S>`, where `S` is the union of the selections from the alternatives of `E`, and `Null` for what has no such entry | never |
| a date, a time, ...        | the type of the property, with the range of its values (Table 67): `month` is `number in [1..12]` | never, or always for a name the kind has not |
| `range<E>`                 | `E \| Null` for `start` and `end`, `boolean` for the flags               | never    |
| `Null`                     | `Null`                                                                   | always   |
| `Any`, a named type        | `Any \| Null`                                                            | maybe    |
| anything else              | `Null`                                                                   | always   |

A union with an alternative that errors and one that does not is a **maybe**; the analyzer warns only
about **always**. A component of the other kind of duration (`duration("P1Y").days`) is an error, as the
TCK flags it, though Table 67 says `null`; `time offset` and `timezone` of a value that has none are `null`
and no error.

A context type of the specification is open (`Feel.Type.Conformance`), and a declared type is one. The type of a
context literal, and of a constant, is **closed** (`{:closed_context, entries}`, D-65): it has the entries it lists
and no others, so the selection of an entry it does not list is `Null` for sure.

Normalization retains nested exclusions. A path reader requests a positive upper approximation
of each union it inspects: the source, list elements, and context entries traversed for descendants.
An excluded context is therefore not mistaken for a value with no properties. This approximation
is local to reading; selecting an entry returns its declared union, including exclusions, unchanged.

## The descendant

`descend/2` is the type of `e...name` (D-65). The result is a list, and what is in it is known as far as the
source is: a closed context is walked as the run walks a value, so `{a: {b: n}}...b` is `list<number | Null>`
and `{a: 1}...b` is `list<Null>` (the list of nothing, Table 43). An open context may have entries that its type does not list,
nested as deep as it likes, so what is in the list is `Any | Null`. The contexts in a list are not looked into
(`Feel.Runtime.Path`), so a type that is a list is not either. A source that is `Null` or not a context is an error.

The `value` property of a date and a time is a `number`. That of a date and time is `number | Null` and a maybe:
it is `null` with an error for one with no zone, which the type does not tell (D-68).

    iex> context = [{:context, %{"age" => [:number, :null]}}]
    iex> Feel.Analysis.Paths.select(context, "age")
    {:ok, [:number, :null], :never}
    iex> {:ok, type, :never} = Feel.Analysis.Paths.select([:date], "month")
    iex> Feel.Type.Union.format(type)
    "number in [1..12]"
    iex> Feel.Analysis.Paths.select([{:context, %{}}], "age")
    {:ok, [:any, :null], :maybe}
    iex> Feel.Analysis.Paths.select([{:closed_context, %{}}], "age")
    {:ok, [:null], :always}

# `errors`

```elixir
@type errors() :: :never | :maybe | :always
```

Whether the run can report an error for the selection, for the values of the type.

# `descend`

```elixir
@spec descend(Feel.Type.Union.t(), Feel.Name.t()) ::
  {:ok, Feel.Type.Union.t(), errors()}
```

The type of the descendants of `key` in a value of type `source`, and whether it can be an error.

    iex> Feel.Analysis.Paths.descend([{:closed_context, %{"a" => [:number]}}], "b")
    {:ok, [{:list, [:null]}], :never}
    iex> {:ok, type, :never} = Feel.Analysis.Paths.descend([{:closed_context, %{"a" => [{:closed_context, %{"b" => [:number]}}]}}], "b")
    iex> Feel.Type.Union.format(type)
    "list<number>"
    iex> Feel.Analysis.Paths.descend([{:context, %{"b" => [:number]}}], "b")
    {:ok, [{:list, [:any, :null]}], :never}

# `select`

```elixir
@spec select(Feel.Type.Union.t(), Feel.Name.t()) ::
  {:ok, Feel.Type.Union.t(), errors()}
```

The type of the selection of `key` from a value of type `source`, and whether it can be an error.

---

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