Witchcraft.Arrow (Witchcraft v1.0.4) View Source

Arrows abstract the idea of computations, potentially with a context.

Arrows are in fact an abstraction above monads, and can be used both to express all other type classes in Witchcraft. They also enable some nice flow-based reasoning about computation.

For a nice illustrated explination, see Haskell/Understanding arrows

Arrows let you think diagrammatically, and is a powerful way of thinking about flow programming, concurrency, and more.

             ---> f --------------------------
             |                                 v
input ---> split                            unsplit ---> result
             |                                 ^
             |              --- h ---        |
             |              |         v        |
             ---> g ---> split     unsplit ---
                            |         ^
                            --- i ---

Type Class

An instance of Witchcraft.Arrow must also implement Witchcraft.Category, and define Witchcraft.Arrow.arrowize/2.

Semigroupoid  [compose/2, apply/2]
    
 Category     [identity/1]
    
  Arrow       [arrowize/2]

Link to this section Summary

Functions

Operator alias for fanout/2.

Operator alias for product/2.

Lift a function into an arrow, much like how of/2 does with data.

Alias for product/2, meant to invoke a spacial metaphor.

Duplicate incoming data into both halves of a 2-tuple, and run one function on the left copy, and a different function on the right copy.

Target the first element of a tuple.

The identity function lifted into an arrow of the correct type.

Compose an arrow (left) with a function (right) to produce a new arrow.

Compose a function (left) with an arrow (right) to produce a new arrow.

Take two arguments (as a 2-tuple), and run one function on the left side (first element), and run a different function on the right side (second element).

Switch the associativity of a nested tuple. Helpful since many arrows act on a subset of a tuple, and you may want to move portions in and out of that stream.

Target the second element of a tuple.

Copy a single value into both positions of a 2-tuple.

Swap positions of elements in a tuple.

Merge two tuple values with a combining function.

Link to this section Types

Specs

t() :: (... -> any())

Link to this section Functions

Specs

t() &&& t() :: t()

Operator alias for fanout/2.

Examples

iex> fanned = fn x -> x - 10 end &&& fn y -> inspect(y) <> "!" end
...> fanned.(42)
{32, "42!"}

iex> fanned =
...>   fn x -> x - 10 end
...>   &&& fn y -> inspect(y) <> "!" end
...>   &&& fn z -> inspect(z) <> "?" end
...>   &&& fn d -> inspect(d) <> inspect(d) end
...>   &&& fn e -> e / 2 end
...>
...> fanned.(42)
{{{{32, "42!"}, "42?"}, "4242"}, 21.0}

Specs

t() ^^^ t() :: t()

Operator alias for product/2.

Examples

iex> arr = fn x -> x - 10 end ^^^ fn y -> y <> "!" end
...> arr.({42, "Hi"})
{32, "Hi!"}

iex> {42, "Hi"} |> (fn x -> x - 10 end ^^^ fn y -> y <> "!" end).()
{32, "Hi!"}

Specs

arrowize(t(), (... -> any())) :: t()

Lift a function into an arrow, much like how of/2 does with data.

Essentially a label for composing functions end-to-end, where instances may have their own special idea of what composition means. The simplest example is a regular function. Others are possible, such as Kleisli arrows.

Examples

iex> use Witchcraft.Arrow
...> times_ten = arrowize(fn -> nil end, &(&1 * 10))
...> 5 |> pipe(times_ten)
50

Specs

beside(t(), t()) :: t()

Alias for product/2, meant to invoke a spacial metaphor.

Examples

iex> beside(&(&1 - 10), &(&1 <> "!")).({42, "Hi"})
{32, "Hi!"}
Link to this function

fanout(arrow_f, arrow_g)

View Source

Specs

fanout(t(), t()) :: t()

Duplicate incoming data into both halves of a 2-tuple, and run one function on the left copy, and a different function on the right copy.

         ------> f.(a) = x ------
         |                        v
a ---> split = {a, a}           {x, y}
         |                        ^
         ------> g.(a) = y ------

Examples

iex> Witchcraft.Semigroupoid.pipe(42, fanout(&(&1 - 10), &(inspect(&1) <> "!")))
{32, "42!"}

Specs

first(t()) :: t()

Target the first element of a tuple.

Examples

iex> first(fn x -> x * 50 end).({1, 1})
{50, 1}

Specs

id_arrow(t()) :: (any() -> t())

The identity function lifted into an arrow of the correct type.

Examples

iex> id_arrow(fn -> nil end).(99)
99

Specs

postcompose(t(), (... -> any())) :: t()

Compose an arrow (left) with a function (right) to produce a new arrow.

Examples

iex> f = postcompose(
...>   arrowize(fn _ -> nil end, fn x -> x + 1 end),
...>   fn y -> y * 10 end
...> )
...> f.(42)
430

Specs

precompose((... -> any()), t()) :: t()

Compose a function (left) with an arrow (right) to produce a new arrow.

Examples

iex> f = precompose(
...>   fn x -> x + 1 end,
...>   arrowize(fn _ -> nil end, fn y -> y * 10 end)
...> )
...> f.(42)
430
Link to this function

product(arrow_f, arrow_g)

View Source

Specs

product(t(), t()) :: t()

Take two arguments (as a 2-tuple), and run one function on the left side (first element), and run a different function on the right side (second element).

  ------> f.(a) = x -------
  |                         v
{a, b}                    {x, y}
  |                         ^
  ------> g.(b) = y -------

Examples

iex> product(&(&1 - 10), &(&1 <> "!")).({42, "Hi"})
{32, "Hi!"}

Specs

reassociate({any(), {any(), any()}} | {{any(), any()}, any()}) ::
  {{any(), any()}, any()} | {any(), {any(), any()}}

Switch the associativity of a nested tuple. Helpful since many arrows act on a subset of a tuple, and you may want to move portions in and out of that stream.

Examples

iex> reassociate({1, {2, 3}})
{{1, 2}, 3}

iex> reassociate({{1, 2}, 3})
{1, {2, 3}}

Specs

second(t()) :: t()

Target the second element of a tuple.

Examples

iex> second(fn x -> x * 50 end).({1, 1})
{1, 50}

Specs

split(any()) :: {any(), any()}

Copy a single value into both positions of a 2-tuple.

This is useful is you want to run functions on the input separately.

Examples

iex> split(42)
{42, 42}

iex> import Witchcraft.Semigroupoid, only: [<~>: 2]
...> 5
...> |> split()
...> |> (second(fn x -> x - 2 end)
...> <~> first(fn y -> y * 10 end)
...> <~> second(&inspect/1)).()
{50, "3"}

iex> use Witchcraft.Arrow
...> 5
...> |> split()
...> |> pipe(second(fn x -> x - 2 end))
...> |> pipe(first(fn y -> y * 10 end))
...> |> pipe(second(&inspect/1))
{50, "3"}

Specs

swap({any(), any()}) :: {any(), any()}

Swap positions of elements in a tuple.

Examples

iex> swap({1, 2})
{2, 1}

Specs

unsplit(
  {any(), any()},
  (any(), any() -> any())
) :: any()

Merge two tuple values with a combining function.

Examples

iex> unsplit({1, 2}, &+/2)
3