# `Taxo`
[🔗](https://github.com/rulvio/taxo/blob/v0.2.0/lib/taxo.ex#L1)

Taxo builds and queries tag hierarchies.

It ports the `derive` and `underive` functions from Clojure's hierarchy
system.

# `t`

```elixir
@type t() :: %Taxo{
  ancestors: %{required(tag()) =&gt; MapSet.t(tag())},
  descendants: %{required(tag()) =&gt; MapSet.t(tag())},
  parents: %{required(tag()) =&gt; MapSet.t(tag())}
}
```

# `tag`

```elixir
@type tag() :: term()
```

A node in a taxonomy. A tag can be any term, because Taxo stores it as a map key.

# `ancestors`

```elixir
@spec ancestors(t(), tag()) :: MapSet.t(tag())
```

Returns the ancestors of `child` in `taxo`.

## Examples

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.ancestors(:monkey)
    MapSet.new([:mammal])

# `derive`

```elixir
@spec derive(t(), tag(), tag()) :: t()
```

Adds a parent/child relationship between `child` and `parent` in `taxo`.

This updates `:parents` directly, then updates `:ancestors` and
`:descendants` for every tag the change affects: `child`, `parent`, and
all their existing relatives.

Raises `Taxo.CyclicDerivationError` if `parent` is already a descendant of
`child`. Adding the relationship would otherwise create a cycle.

## Examples

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal)
    %Taxo{
      ancestors: %{monkey: MapSet.new([:mammal])},
      parents: %{monkey: MapSet.new([:mammal])},
      descendants: %{mammal: MapSet.new([:monkey])}
    }

# `descendants`

```elixir
@spec descendants(t(), tag()) :: MapSet.t(tag())
```

Returns the descendants of `child` in `taxo`.

## Examples

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.descendants(:mammal)
    MapSet.new([:monkey])

# `is_a?`

```elixir
@spec is_a?(t(), tag(), tag()) :: boolean()
```

Returns true if `child` and `parent` are equal.
Returns true if `child` is a direct or indirect descendant of `parent`.
Returns false otherwise.

## Examples

    iex> Taxo.new |> Taxo.is_a?(:monkey, :monkey)
    true

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.derive(:mammal, :vertebrate) |> Taxo.is_a?(:monkey, :vertebrate)
    true

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.derive(:mammal, :vertebrate) |> Taxo.is_a?(:vertebrate, :monkey)
    false

# `new`

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

Creates a new, empty taxonomy.

Use `derive/3` to add parent/child relationships to it.

## Examples

    iex> Taxo.new
    %Taxo{ancestors: %{}, parents: %{}, descendants: %{}}

# `parents`

```elixir
@spec parents(t(), tag()) :: MapSet.t(tag())
```

Returns the direct parents of `child` in `taxo`.

## Examples

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.parents(:monkey)
    MapSet.new([:mammal])

# `underive`

```elixir
@spec underive(t(), tag(), tag()) :: t()
```

Removes a parent/child relationship between `child` and `parent` in `taxo`.

This drops the `child`/`parent` link, then rebuilds the whole taxonomy
from the pairs that remain, calling `derive/3` on each pair in turn.

The rebuild cost depends on the size of the whole taxonomy, not on the
size of the link removed. `underive/3` is not a cheap operation on a
large hierarchy.

## Examples

    iex> Taxo.new |> Taxo.derive(:monkey, :mammal) |> Taxo.underive(:monkey, :mammal)
    %Taxo{ancestors: %{}, parents: %{}, descendants: %{}}

---

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