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.
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.
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}
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}
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!"}