monok v0.2.0 Monok View Source
Monad on :ok
Provides the infix pipe operators ~>, ~>>, and <~> for writing clean pipelines that treat {:ok, result}
and {:error, reason} tuples like functors, monads or applicatives.
Also provides the functions fmap, bind and lift as which are functionally identical but are less cryptic and
can be used without overriding any inifix operators which could potentially conflict with other libraries.
Why would you ever do this?
Whilst writing unnecessary macros and overriding infix operators are both generally considered bad practice I
thought I’d try this out given just how freqently {:ok, result} and {:error, reason} tuples are encountered
in Elixir.
Functor Pipelines
Allows you to write clean pipelines that transforms values inside of {:ok, value} tuples.
iex> {:ok, [1, 2, 3]}
...> ~> Enum.sum()
...> ~> div(2)
{:ok, 3}
If the input is an {:error, reason} tuple it is carried through the pipeline without applying any
transformations.
iex> {:error, :reason}
...> ~> Enum.sum()
...> ~> div(2)
{:error, :reason}
Monad Pipelines
Allows you to write clean pipelines that transform values in {:ok, value} tuples with functions that also
return {:ok, value} tuples.
iex> decrement = fn
...> x when x > 0 -> {:ok, x - 1}
...> _ -> {:error, :input_too_small}
...> end
iex> {:ok, 3}
...> ~>> decrement.()
...> ~>> decrement.()
{:ok, 1}
If at any point in the pipeline an {:error, reason} tuple is returned it is carried through without
any of the transformation functions being applied.
iex> decrement = fn
...> x when x > 0 -> {:ok, x - 1}
...> _ -> {:error, :input_too_small}
...> end
iex>
...> {:ok, 3}
...> ~>> (fn _ -> {:error, :contrived_example} end).()
...> ~>> decrement.()
...> ~>> decrement.()
{:error, :contrived_example}
Mixed Pipelines
These pipe operators don’t have to be used in seperate pipelines but can be used together or even with the |>
standard pipe operator.
iex> 7
...> |> (&(if &1 > 5, do: {:ok, &1}, else: {:error, :too_low})).()
...> ~> Integer.to_string()
...> ~>> (&(if &1 |> String.length() > 0, do: {:ok, &1 <> "!"}, else: {:error, :empty_string})).()
{:ok, "7!"}
Link to this section Summary
Functions
Infix lift operator
Applies a function that returns a value wrapped in an :ok tuple to a value wrapped in an :ok tuple
Applies a function to a value wrapped in an ok tuple, has no effect if given an error tuple
Applies a function wrapped in an :ok tuple to a value wrapped in an :ok tuple
Infix fmap operator
Infix bind operator
Link to this section Functions
Infix lift operator.
Treats the function_tuple as an applicative.
Applies a function wrapped in an :ok tuple to a value wrapped in an :ok tuple.
Carries through an :error tuple if either the value or function arguments are given as :error tuples instead of :ok tuples.
Examples
iex> {:ok, [1, 2, 3]}
...> <~> {:ok, &Enum.sum/1}
{:ok, 6}
iex> {:ok, 1}
...> <~> {:error, :reason}
{:error, :reason}
iex> {:error, :reason}
...> <~> {:ok, &Enum.sum/1}
{:error, :reason}
Applies a function that returns a value wrapped in an :ok tuple to a value wrapped in an :ok tuple.
Carries through an :error tuple if either the value argument is given as an :error tuple or the function returns an :error tuple when applied to the value.
Examples
iex> {:ok, [1, 2, 3]}
...> |> Monok.bind(fn x -> {:ok, Enum.sum(x)} end)
{:ok, 6}
iex> {:ok, [1, 2, 3]}
...> |> Monok.bind(fn _ -> {:error, :reason} end)
{:error, :reason}
iex> {:error, :reason}
...> |> Monok.bind(fn x -> {:ok, Enum.sum(x)} end)
{:error, :reason}
Applies a function to a value wrapped in an ok tuple, has no effect if given an error tuple.
Examples
iex> {:ok, [1, 2, 3]}
...> |> Monok.fmap(&Enum.sum/1)
{:ok, 6}
iex> {:error, :reason}
...> |> Monok.fmap(&Enum.sum/1)
{:error, :reason}
Applies a function wrapped in an :ok tuple to a value wrapped in an :ok tuple.
Carries through an :error tuple if either the value or function arguments are given as :error tuples instead of :ok tuples.
Examples
iex> {:ok, [1, 2, 3]}
...> |> Monok.lift({:ok, &Enum.sum/1})
{:ok, 6}
iex> {:ok, 1}
...> |> Monok.lift({:error, :reason})
{:error, :reason}
iex> {:error, :reason}
...> |> Monok.lift({:ok, &Enum.sum/1})
{:error, :reason}
Infix fmap operator.
Treats input as value_tuple a functor.
Applies a function to a value wrapped in an ok tuple, has no effect if given an error tuple.
Examples
iex> {:ok, [1, 2, 3]}
...> ~> Enum.sum()
{:ok, 6}
iex> {:error, :reason}
...> ~> Enum.sum()
{:error, :reason}
Infix bind operator.
Treats the value_tuple and tuple_function as monads.
Applies a function that returns a value wrapped in an :ok tuple to a value wrapped in an :ok tuple.
Carries through an :error tuple if either the value argument is given as an :error tuple or the function returns an :error tuple when applied to the value.
Examples
iex> {:ok, [1, 2, 3]}
...> ~>> (fn x -> {:ok, Enum.sum(x)} end).()
{:ok, 6}
iex> {:ok, [1, 2, 3]}
...> ~>> (fn _ -> {:error, :reason} end).()
{:error, :reason}
iex> {:error, :reason}
...> ~>> (fn x -> {:ok, Enum.sum(x)} end).()
{:error, :reason}