View Source WarmFuzzyThing.Either (WarmFuzzyThing v0.1.0)
The Either monad is a union between two general notions: Left and Right (the naming is inherited from Haskell and it depicts the idea: "Either I have this or I have that.").
This means that an Either monad can either be a Left or it can be a Right (think of it as a simple union between two types).
A Right is represented by the well known success type tuple {:ok, value} when value: any(),
where as a Left is represented by the well known error type tuple {:error, reason} when reason: any().
Generally a Right Either represents a successful transformation/operation where as a Left Either represents an unsuccessful one.
Either exports a set of function for ease of chaining functions (transformations):
WarmFuzzyThing.Either.fmap/2- Used for applying a function over the value of aEithermonad;WarmFuzzyThing.Either.bind/2- Used for applying a function over a value inside aEithermonad that returns a brand newEithermonad;WarmFuzzyThing.Either.fold/3- Used for either returning a default value or applying a function over the value inside theEithermonad;
example
Example
iex> WarmFuzzyThing.Either.fmap({:ok, 1}, fn v -> v + 1 end)
{:ok, 2}
iex> WarmFuzzyThing.Either.fmap({:error, :not_found}, fn v -> v + 1 end)
{:error, :not_found}
iex> WarmFuzzyThing.Either.bind({:ok, 1}, fn v -> {:ok, v + 1} end)
{:ok, 2}
iex> WarmFuzzyThing.Either.bind({:error, :not_found}, fn v -> {:ok, v + 1} end)
{:error, :not_found}
iex> WarmFuzzyThing.Either.bind({:ok, 1}, fn v -> {:error, :not_found} end)
{:error, :not_found}
iex> WarmFuzzyThing.Either.fold({:ok, 1}, &WarmFuzzyThing.id/1)
1
iex> WarmFuzzyThing.Either.fold({:error, :not_found}, &WarmFuzzyThing.id/1)
nil
iex> WarmFuzzyThing.Either.fold({:error, :not_found}, :empty, &WarmFuzzyThing.id/1)
:emptyThis set of function are setup in a way to allow for easy pipelining.
example-1
Example
iex> {:ok, "elixir"}
...> |> WarmFuzzyThing.Either.fmap(fn v -> v <> " with monads" end)
...> |> WarmFuzzyThing.Either.fmap(&String.length/1)
...> |> WarmFuzzyThing.Either.bind(fn
...> v when v <= 20 -> {:ok, v}
...> _ -> {:error, :too_big}
...> end)
...> |> WarmFuzzyThing.Either.fold(&WarmFuzzyThing.id/1)
18Either exports a set of operators for handling the chaining functions:
~>representsWarmFuzzyThing.Either.fmap/2~>>representsWarmFuzzyThing.Either.bind/2<~>representsWarmFuzzyThing.Either.fold/3
example-2
Example
iex> import WarmFuzzyThing.Either, only: [~>: 2, ~>>: 2, <~>: 2]
iex> {:ok, "elixir"} ~> &String.length/1
{:ok, 6}
iex> {:error, :not_found} ~> &String.length/1
{:error, :not_found}
iex> {:ok, "elixir"} ~>> &({:ok, String.length(&1)})
{:ok, 6}
iex> {:ok, ""} ~>> fn "" -> {:error, :empty}; v -> {:ok, String.length(v)} end
{:error, :empty}
iex> {:ok, "elixir"} <~> &String.length/1
6
iex> {:error, :not_a_number} ~>> fn v -> {:ok, v + 1} end
{:error, :not_a_number}
iex> {:error, :empty} <~> fn v -> v + 1 end
nil
iex> {:error, :empty} <~> {:not_found, fn v -> v + 1 end}
:not_found
iex> {:ok, "elixir"}
...> ~> fn v -> v <> " with monads" end
...> ~> fn v -> v <> " is awesome" end
...> ~>> fn v when v < 20 -> {:error, :too_short}; v -> {:ok, String.length(v)} end
...> <~> {0, &WarmFuzzyThing.id/1}
29
Link to this section Summary
Functions
Operator for handling WarmFuzzyThing.Either.fold/3.
Apply a function over the 'right' portion (the "success" value) of an Either monad.
The result of the function will be a brand new Either monad.
This means that the value inside the monad can change from 'left' to 'right' and vice versa.
Apply a function over the 'right' portion (the "success" value) of an Either monad.
The result of the function will be the new 'right' portion of the Either monad.
Depending of the value inside the Either monad, fold will either (no pun intended)
Checks whether a Either monad is 'left'
Similar to how Either.fmap/2 works, but it maps over the Left value of the Either monad.
Call a 'void' type of function if the Either monad is a 'left'.
WarmFuzzyThing.Either.on_left/2 returns the Either as is. No changes are applied.
Call a 'void' type of function if the Either monad is a 'right'.
WarmFuzzyThing.Either.on_right/2 returns the Either as is. No changes are applied.
Checks whether a Either monad is 'right'
Cycles through a sequence of EIthers, if it reaches a 'left' Either it short circuits and returns it.
Operator for handling WarmFuzzyThing.Either.map/2.
Operator for handling WarmFuzzyThing.Either.bind/2.
Link to this section Types
@type t(reason, value) :: {:error, reason} | {:ok, value}
Link to this section Functions
@spec t(reason, value) <~> ({default :: new_value, function} | function) :: new_value when reason: any(), value: any(), function: (value -> new_value), new_value: any()
Operator for handling WarmFuzzyThing.Either.fold/3.
example
Example
iex> import WarmFuzzyThing.Either, only: [<~>: 2]
iex> {:ok, 1} <~> fn v -> v + 1 end
2
iex> {:ok, "hello"} <~> fn _v -> "world" end
"world"
iex> {:ok, "hello"} <~> &String.length/1
5
iex> {:error, :not_a_number} <~> fn v -> v + 1 end
nil
iex> {:error, :not_a_number} <~> {:not_found, fn v -> v + 1 end}
:not_found
@spec bind(t(reason, value), function) :: new_either when reason: any(), value: any(), function: (value -> new_either), new_either: t(new_reason, new_value), new_value: any(), new_reason: any()
Apply a function over the 'right' portion (the "success" value) of an Either monad.
The result of the function will be a brand new Either monad.
This means that the value inside the monad can change from 'left' to 'right' and vice versa.
If an Either monad with a 'left' value is passed, the function is not invoked,
WarmFuzzyThing.Either.bind/2 just return the 'left' value.
example
Example
iex> WarmFuzzyThing.Either.bind({:ok, 1}, fn v -> {:ok, v + 1} end)
{:ok, 2}
iex> WarmFuzzyThing.Either.bind({:ok, "hello"}, fn _v -> {:ok, "world"} end)
{:ok, "world"}
iex> WarmFuzzyThing.Either.bind({:ok, "hello"}, &({:ok, String.length(&1)}))
{:ok, 5}
iex> WarmFuzzyThing.Either.bind({:ok, "hello"}, fn _v -> {:error, "something went wrong"} end)
{:error, "something went wrong"}
iex> WarmFuzzyThing.Either.fmap({:error, :not_a_number}, fn v -> {:ok, v + 1} end)
{:error, :not_a_number}
@spec fmap(t(reason, value), function) :: t(reason, new_value) when reason: any(), value: any(), function: (value -> new_value), new_value: any()
Apply a function over the 'right' portion (the "success" value) of an Either monad.
The result of the function will be the new 'right' portion of the Either monad.
If an Either monad with a 'left' value is passed, the function is not invoked,
WarmFuzzyThing.Either.map/2 just return the 'left' value.
example
Example
iex> WarmFuzzyThing.Either.fmap({:ok, 1}, fn v -> v + 1 end)
{:ok, 2}
iex> WarmFuzzyThing.Either.fmap({:ok, "hello"}, fn _v -> "world" end)
{:ok, "world"}
iex> WarmFuzzyThing.Either.fmap({:ok, "hello"}, &String.length/1)
{:ok, 5}
iex> WarmFuzzyThing.Either.fmap({:error, :not_a_number}, fn v -> v + 1 end)
{:error, :not_a_number}
@spec fold(t(reason, value), default :: new_value, function) :: new_value when reason: any(), value: any(), function: (value -> new_value), new_value: any()
Depending of the value inside the Either monad, fold will either (no pun intended):
- Apply the given function over the
'right'value and return the value itself; - Return the
'default'value given to the function. If no'default'value is provided,nilis returned;
example
Example
iex> WarmFuzzyThing.Either.fold({:ok, 1}, fn v -> v + 1 end)
2
iex> WarmFuzzyThing.Either.fold({:ok, "hello"}, fn _v -> "world" end)
"world"
iex> WarmFuzzyThing.Either.fold({:ok, "hello"}, &String.length/1)
5
iex> WarmFuzzyThing.Either.fold({:error, :not_a_number}, fn v -> v + 1 end)
nil
iex> WarmFuzzyThing.Either.fold({:error, :not_a_number}, :not_found, fn v -> v + 1 end)
:not_found
Checks whether a Either monad is 'left'
example
Example
iex> import WarmFuzzyThing.Either iex> left?({:error, :empty}) true iex> left?({:ok, 1}) false
@spec map_left(t(reason, value), (reason -> new_reason)) :: t(new_reason, value) when reason: any(), value: any(), new_reason: any()
Similar to how Either.fmap/2 works, but it maps over the Left value of the Either monad.
Useful when you want to change the structure of the reason.
example
Example
iex> WarmFuzzyThing.Either.map_left({:ok, 1}, fn reason -> {:operation, reason} end)
{:ok, 1}
iex> WarmFuzzyThing.Either.map_left({:error, :not_found}, fn reason -> {:user, reason} end)
{:error, {:user, :not_found}}
@spec on_left(t(reason, value), function) :: t(reason, value) when reason: any(), value: any(), function: (reason -> :ok)
Call a 'void' type of function if the Either monad is a 'left'.
WarmFuzzyThing.Either.on_left/2 returns the Either as is. No changes are applied.
If the Either has a 'right' value inside, the function is not invoked.
example
Example
iex> WarmFuzzyThing.Either.on_left({:ok, 1}, &IO.inspect/1)
{:ok, 1}
iex> WarmFuzzyThing.Either.on_left({:error, :not_a_number}, &IO.inspect/1)
:not_a_number
{:error, :not_a_number}
@spec on_right(t(reason, value), function) :: t(reason, value) when reason: any(), value: any(), function: (value -> :ok)
Call a 'void' type of function if the Either monad is a 'right'.
WarmFuzzyThing.Either.on_right/2 returns the Either as is. No changes are applied.
If the Either has a 'left' value inside, the function is not invoked.
example
Example
iex> WarmFuzzyThing.Either.on_right({:ok, 1}, &IO.inspect/1)
1
{:ok, 1}
iex> WarmFuzzyThing.Either.on_right({:error, :not_a_number}, &IO.inspect/1)
{:error, :not_a_number}
Checks whether a Either monad is 'right'
example
Example
iex> import WarmFuzzyThing.Either iex> right?({:ok, 1}) true iex> right?({:error, :empty}) false
Cycles through a sequence of EIthers, if it reaches a 'left' Either it short circuits and returns it.
Otherwise returns a Either with a list of all values.
example
Example
iex> WarmFuzzyThing.Either.sequence([{:ok, 1}, {:ok, 2}])
{:ok, [1, 2]}
iex> WarmFuzzyThing.Either.sequence([])
{:ok, []}
iex> WarmFuzzyThing.Either.sequence([{:ok, 1}, {:error, :not_found}, {:ok, 2}])
{:error, :not_found}
iex> WarmFuzzyThing.Either.sequence([{:error, :not_found}, {:error, :empty}])
{:error, :not_found}
@spec t(reason, value) ~> function :: t(reason, new_value) when reason: any(), value: any(), function: (value -> new_value), new_value: any()
Operator for handling WarmFuzzyThing.Either.map/2.
example
Example
iex> import WarmFuzzyThing.Either, only: [~>: 2]
iex> {:ok, 1} ~> fn v -> v + 1 end
{:ok, 2}
iex> {:ok, "hello"} ~> fn _v -> "world" end
{:ok, "world"}
iex> {:ok, "hello"} ~> &String.length/1
{:ok, 5}
iex> {:error, :not_a_number} ~> fn v -> v + 1 end
{:error, :not_a_number}
@spec t(reason, value) ~>> function :: t(new_reason, new_value) when reason: any(), value: any(), function: (value -> new_either), new_either: t(new_reason, new_value), new_value: any(), new_reason: any()
Operator for handling WarmFuzzyThing.Either.bind/2.
example
Example
iex> import WarmFuzzyThing.Either, only: [~>>: 2]
iex> {:ok, 1} ~>> fn v -> {:ok, v + 1} end
{:ok, 2}
iex> {:ok, "hello"} ~>> fn _v -> {:ok, "world"} end
{:ok, "world"}
iex> {:ok, "hello"} ~>> &({:ok, String.length(&1)})
{:ok, 5}
iex> {:ok, "hello"} ~>> fn _v -> {:error, "something went wrong"} end
{:error, "something went wrong"}
iex> {:error, :not_a_number} ~>> fn v -> {:ok, v + 1} end
{:error, :not_a_number}