Packages

FunLand adds Behaviours to define Algebraic Data Types ('Container' data types) to Elixir, such as Functors, Monoids and Monads.

Current section

Files

Jump to
fun_land lib fun_landic either.ex
Raw

lib/fun_landic/either.ex

defmodule FunLandic.Either do
@moduledoc """
An implementation of the "Either" monad.
`Either` is very similar to `Maybe`, with the difference that instead of returning 'Maybe.nothing' on failure,
you can specify what to return on failure at each step, so you know where something failed.
TODO:
- Verify implementation.
- Rename to something less ambigous than 'Either'?
"""
use FunLand
use FunLand.Monad
use FunLand.Reducable
defstruct [:val, right?: true]
alias __MODULE__
defimpl Inspect do
def inspect(%Either{right?: false, val: val}, _opts), do: "Either.left(#{inspect val})"
def inspect(%Either{right?: true , val: val}, _opts), do: "Either.right(#{inspect val})"
end
def right(val) do
%Either{val: val, right?: true}
end
def left(val) do
%Either{val: val, right?: false}
end
def left?(either)
def left?(%Either{right?: false}), do: true
def left?(%Either{right?: true}), do: false
def right?(either)
def right?(%Either{right?: false}), do: false
def right?(%Either{right?: true}), do: true
@doc """
Converts the %Either{} in the `{:ok, value} | {:error, reason}`-format.
"""
def to_success_tuple(%Either{right?: false, val: val}) do
{:error, val}
end
def to_success_tuple(%Either{right?: true, val: val}) do
{:ok, val}
end
@doc """
Turns the common `{:ok, value} | {:error, reason}`-format into an %Either{}.
"""
def from_success_tuple({:ok, val}), do: right(val)
def from_success_tuple(:error), do: left(nil)
def from_success_tuple({:error, val}), do: left(val)
def run_either(either, function_if_left, function_if_right)
def run_either(%Either{right?: false, val: val}, function_if_left, _), do: function_if_left.(val)
def run_either(%Either{right?: true, val: val}, _, function_if_right), do: function_if_right.(val)
# Monad behaviour implementations
def map(left = %Either{right?: false}, fun), do: left
def map(%Either{right?: true, val: val}, fun), do: new(fun.(val))
def apply_with(left = %Either{right?: false}, _), do: left
def apply_with(%Either{right?: true, val: fun}, right = %Either{}), do: map(right, fun)
def new(val), do: right(val)
def chain(either = %Either{right?: true, val: val}, fun) do
fun.(val)
end
def chain(either = %Either{right?: false, val: val}, _fun) do
either
end
# Reducable behavior implementations
def reduce(either, acc, fun)
def reduce(either = %Either{right?: false}, acc, _fun), do: acc
def reduce(either = %Either{right?: true, val: val}, acc, fun), do: fun.(val, acc)
end