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.

do-notation enhanced with a return operation.

Link to this section Types

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

Specs

Alias for async_chain/2

Link to this function

async_chain(chainable, link)

View Source

Specs

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

Specs

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