defmodule Algae.Free do
  use Quark.Partial

  @type t :: Algae.Free.Pure.t | Algae.Free.Wrap.t

  defmodule Pure do
    @type t :: %Algae.Free.Pure{pure: any}
    defstruct [:pure]
  end

  defmodule Wrap do
    @type t :: %Algae.Free.Wrap{wrap: any}

    @moduledoc ~S"""
    `wrap` must be some functor wrapping another `%Free.X{}`

    ```elixir

    %Free.Wrap{wrap: %Id{id: %Free.Pure{pure: "Terminus"}}

    ```

    """
    defstruct [:wrap]
  end

  @spec wrap(any) :: Algae.Free.Wrap.t
  defpartial wrap(value), do: %Algae.Free.Wrap{wrap: value}
end

defmodule Algae.Free do
  use Quark.Partial

  @type t :: Algae.Free.Pure.t | Algae.Free.Deep.t

  defmodule Shallow do
    @type t :: %Algae.Free.Shallow{shallow: any}
    defstruct [:shallow]
  end

  defmodule Deep do
    @moduledoc ~S"""
    Deep holds two values: a value (often a functor) in `deep`, and another
    `Algae.Free.t` in `deeper`.

    ```elixir

    %Free.Deep{deep: %Id{id: 42}, deeper: %Free.Shallow{shallow: "Terminus"}}

    ```

    """

    @type t :: %Algae.Free.Deep{deep: any, deeper: Algae.Free.t}

    defstruct [:deep, :deeper]
  end

  @spec wrap(any) :: Algae.Free.Wrap.t
  defpartial wrap(value), do: %Algae.Free.Wrap{wrap: value}
end

Rework %Free with more explicit structure
Rework %Free with more explicit structure

robot-overlord/algae