Witchcraft v1.0.0 Witchcraft.Monad 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 convenuenct 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

do-notation enhanced with a return operation

Link to this section Functions

Link to this macro async(sample, list) View Source (macro)

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]
Link to this function async_bind(chainable, link) View Source

Alias for async_chain/2

Link to this function async_chain(chainable, link) View Source
async_chain(Chain.t, Chain.link) :: Chain.t
async_chain(Chain.t, Chain.link) :: 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
Link to this function async_draw(link, chainable) View Source
async_draw(Chain.t, Chain.link) :: 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
Link to this macro monad(sample, list) View Source (macro)

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
...>  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]