defmodule Result do @type result :: any @type reason :: any @type ok :: {:ok, result} @type error :: {:error, reason} @type t :: ok | error @spec ok(result) :: ok def ok(result) do {:ok, result} end @spec fail(reason) :: error def fail(reason) do {:error, reason} end @spec and_then(t, (result -> t)) :: t def and_then({:error, _reason} = m, _callback), do: m def and_then({:ok, result}, callback) do callback.(result) end @spec and_then(t, (result -> result)) :: t def map({:error, _reason} = m, _updater), do: m def map({:ok, result}, updater) do ok updater.(result) end @spec and_then(t, (reason -> reason)) :: t def map_error({:ok, _result} = m, _updater), do: m def map_error({:error, reason} = m, updater) do fail updater.(reason) end end defmodule TestModule do def fetch do Result.ok 9.0 end def div_ten_by_x(x) do case x do 0.0 -> Result.fail "zero" _ -> {:ok, 10 / x} # tuples are ok too end end end TestModule.fetch |> IO.inspect # {:ok, 9.0} |> Result.map(&(&1 + 1)) |> IO.inspect # {:ok, 10.0} |> Result.and_then(&TestModule.div_ten_by_x/1) |> IO.inspect # {:ok, 1.0} |> Result.map(&(&1 - 1)) |> IO.inspect # {:ok, 0.0} |> Result.and_then(&TestModule.div_ten_by_x/1) |> IO.inspect # {:error, "zero"} |> Result.map(&(&1 + 1)) |> IO.inspect # {:error, "zero"} |> Result.and_then(&TestModule.div_ten_by_x/1) |> IO.inspect # {:error, "zero"} |> Result.map_error(&String.capitalize/1) |> IO.inspect # {:error, "Zero"}