Witchcraft.Monad (Witchcraft v1.0.4) View Source
Very similar to Chain
, Monad
provides a way to link actions, and a way
to bring plain values into the correct context (Applicative
).
This allows us to view actions in a full framework along the lines of functor and applicative:
data ---------------- function ----------------------------> result
| | |
of(Container, data) of/2, or similar of(Container, result)
↓ ↓ ↓
%Container<data> --- (data -> %Container<updated_data>) ---> %Container<updated_data>
As you can see, the linking function may just be of
now that we have that.
For a nice, illustrated introduction, see Functors, Applicatives, And Monads In Pictures.
Having of
also lets us enhance do-notation with a convenient return
function (see monad/2
)
Type Class
An instance of Witchcraft.Monad
must also implement Witchcraft.Applicative
and Wicthcraft.Chainable
.
Functor [map/2]
↓
Apply [convey/2]
↓ ↓
[of/2] Applicative Chain [chain/2]
↓ ↓
Monad
[_]
Link to this section Summary
Functions
Variant of monad/2
where each step internally occurs asynchonously, but lines
run strictly one after another.
Alias for async_chain/2
Asynchronous variant of Witchcraft.Chain.chain/2
.
Asynchronous variant of Witchcraft.Chain.draw/2
.
do-notation enhanced with a return
operation.
Link to this section Types
Specs
t() :: any()
Link to this section Functions
Variant of monad/2
where each step internally occurs asynchonously, but lines
run strictly one after another.
Examples
iex> async [] do
...> [1, 2, 3]
...> end
[1, 2, 3]
iex> async [] do
...> [1, 2, 3]
...> [4, 5, 6]
...> [7, 8, 9]
...> end
[
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9
]
iex> async [] do
...> Witchcraft.Applicative.of([], 1)
...> end
[1]
iex> async [] do
...> a <- [1,2,3]
...> b <- [4,5,6]
...> return(a * b)
...> end
[
4, 5, 6,
8, 10, 12,
12, 15, 18
]
iex> async [] do
...> a <- return 1
...> b <- return 2
...> return(a + b)
...> end
[3]
Specs
async_bind(Witchcraft.Chain.t(), Witchcraft.Chain.link()) :: Witchcraft.Chain.t()
Alias for async_chain/2
Specs
async_chain(Witchcraft.Chain.t(), Witchcraft.Chain.link()) :: Witchcraft.Chain.t()
Asynchronous variant of Witchcraft.Chain.chain/2
.
Note that each async_chain
call awaits that step's completion. This is a
feature not a bug, since chain
can introduce dependencies between nested links.
However, this means that the async features on only really useful on larger data sets,
because otherwise we're just sparking tasks and immediaetly waiting a single application.
Examples
iex> async_chain([1, 2, 3], fn x -> [x, x] end)
[1, 1, 2, 2, 3, 3]
iex> async_chain([1, 2, 3], fn x ->
...> async_chain([x + 1], fn y ->
...> [x * y]
...> end)
...> end)
[2, 6, 12]
0..10_000
|> Enum.to_list()
|> async_chain(fn x ->
async_chain([x + 1], fn y ->
Process.sleep(500)
[x * y]
end)
end)
#=> [0, 2, 6, 12, 20, 30, 42, ...] in around a second
Specs
async_draw(Witchcraft.Chain.t(), Witchcraft.Chain.link()) :: Witchcraft.Chain.t()
Asynchronous variant of Witchcraft.Chain.draw/2
.
Note that each async_draw
call awaits that step's completion. This is a
feature not a bug, since chain
can introduce dependencies between nested links.
However, this means that the async features on only really useful on larger data sets,
because otherwise we're just sparking tasks and immediaetly waiting a single application.
Examples
iex> async_draw(fn x -> [x, x] end, [1, 2, 3])
[1, 1, 2, 2, 3, 3]
iex> (fn y -> [y * 5, y * 10] end)
...> |> async_draw(fn x -> [x, x] end
...> |> async_draw([1, 2, 3])) # note the "extra" closing paren
[5, 10, 5, 10, 10, 20, 10, 20, 15, 30, 15, 30]
iex> fn x ->
...> fn y ->
...> [x * y]
...> end
...> |> async_draw([x + 1])
...> end
...> |> async_draw([1, 2, 3])
[2, 6, 12]
fn x ->
fn y ->
Process.sleep(500)
[x * y]
end
|> async_draw([x + 1])
end
|> async_draw(Enum.to_list(0..10_000))
[0, 2, 6, 12, ...] # in under a second
do-notation enhanced with a return
operation.
return
is the simplest possible linking function, providing the correct of/2
instance for your monad.
Examples
iex> monad [] do
...> [1, 2, 3]
...> end
[1, 2, 3]
iex> monad [] do
...> [1, 2, 3]
...> [4, 5, 6]
...> [7, 8, 9]
...> end
[
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9,
7, 8, 9
]
iex> monad [] do
...> Witchcraft.Applicative.of([], 1)
...> end
[1]
iex> monad [] do
...> return 1
...> end
[1]
iex> monad [] do
...> monad {999} do
...> return 1
...> end
...> end
{1}
iex> monad [] do
...> a <- [1,2,3]
...> b <- [4,5,6]
...> return(a * b)
...> end
[
4, 5, 6,
8, 10, 12,
12, 15, 18
]
iex> monad [] do
...> a <- return 1
...> b <- return 2
...> return(a + b)
...> end
[3]