import TypeClass

defclass Witchcraft.Category do
  @moduledoc """
  A category is some collection of objects and relationships (morphisms) between them.

  This idea is captured by the notion of an identity function for objects,
  and the ability to compose relationships between objects. In most cases,
  these are very straightforward, and composition and identity are the standard
  functions from the `Quark` package or similar.

  ## Type Class

  An instance of `Witchcraft.Category` must also implement `Witchcraft.Semigroupoid`,
  and define `Witchcraft.Category.identity/1`.

      Semigroupoid  [compose/2, apply/2]
          ↓
       Category     [identity/1]
  """

  alias __MODULE__
  alias Witchcraft.Semigroupoid

  extend Witchcraft.Semigroupoid

  use Witchcraft.Internal, deps: [Witchcraft.Semigroupoid]

  @type t :: any()

  where do
    @doc """
    Take some value and return it again.

    ## Examples

        iex> classic_id = identity(fn -> nil end)
        ...> classic_id.(42)
        42

    """
    @spec identity(Category.t()) :: Category.t()
    def identity(category)
  end

  defalias id(category), as: :identity

  properties do
    def left_identity(data) do
      a = generate(data)
      ident = Semigroupoid.compose(Category.identity(a), a)

      equal?(a, ident)
    end

    def right_identity(data) do
      a = generate(data)
      ident = Semigroupoid.compose(a, Category.identity(a))

      equal?(a, ident)
    end
  end
end

definst Witchcraft.Category, for: Function do
  def identity(_), do: &Quark.id/1
end
