defmodule WarmFuzzyThing do
@moduledoc """
Provides a way of working with already known Elixir constructs through monads.
`WarmFuzzyThing.Maybe` represents the Union `nil | {:ok, value} when value: any()`.
`WarmFuzzyThing.Either` represents the Union `{:error, reason} | {:ok, value} when reason: any(), value: any()`.
_To understand more about their general idea and implementation details, head over to their respective documentation._
"""
alias WarmFuzzyThing.Either
alias WarmFuzzyThing.Maybe
@type t(a) :: Either.t(any(), a) | Maybe.t(a)
@callback pure(a) :: WarmFuzzyThing.t(a) when a: any()
@callback fmap(WarmFuzzyThing.t(a), (a -> b)) :: WarmFuzzyThing.t(b) when a: any(), b: any()
@callback bind(WarmFuzzyThing.t(a), (a -> WarmFuzzyThing.t(b))) :: WarmFuzzyThing.t(b) when a: any(), b: any()
@callback fold(WarmFuzzyThing.t(a), default :: b, (a -> b)) :: b when a: any(), b: any()
@callback sequence([WarmFuzzyThing.t(a)]) :: WarmFuzzyThing.t([a]) when a: any()
@doc """
Functions that return the value itself.
## Example
iex> WarmFuzzyThing.id(10)
10
iex> WarmFuzzyThing.id({:ok, 10})
{:ok, 10}
iex> WarmFuzzyThing.id({:error, :not_found})
{:error, :not_found}
iex> WarmFuzzyThing.id(nil)
nil
"""
@spec id(value) :: value when value: any()
def id(value), do: value
@doc """
Converts a `WarmFuzzyThing.Maybe` to `WarmFuzzyThing.Either`
## Example
iex> WarmFuzzyThing.maybe_to_either({:ok, 1}, :not_found)
{:ok, 1}
iex> WarmFuzzyThing.maybe_to_either(nil, :not_found)
{:error, :not_found}
"""
@spec maybe_to_either(Maybe.t(value), reason) :: Either.t(reason, value)
when value: any(),
reason: any()
def maybe_to_either(maybe, reason)
def maybe_to_either(nil, reason), do: {:error, reason}
def maybe_to_either({:ok, value}, _), do: {:ok, value}
@doc """
Converts a `WarmFuzzyThing.Either` to `WarmFuzzyThing.Maybe`
## Example
iex> WarmFuzzyThing.either_to_maybe({:ok, 1})
{:ok, 1}
iex> WarmFuzzyThing.either_to_maybe({:error, :not_found})
nil
"""
@spec either_to_maybe(Either.t(reason, value)) :: Maybe.t(value)
when value: any(),
reason: any()
def either_to_maybe(either)
def either_to_maybe({:error, _reason}), do: nil
def either_to_maybe({:ok, value}), do: {:ok, value}
end