Witchcraft v1.0.0-beta Witchcraft.Apply View Source

An extension of Witchcraft.Functor, Apply provides a way to map functions to their arguments when both are wrapped in the same kind of container.

For a nice, illustrated introduction, see Functors, Applicatives, And Monads In Pictures.

Graphically

If function application looks like like

data |> function == result

and a functor looks like this

%Container<data> ~> function == %Container<result>

then an apply looks like

%Container<data> ~>> %Container<function> == %Container<result>

which is similar to function application inside containers, plus the ability to attach special effects to applications.

           data --------------- function ---------------> result
%Container<data> --- %Container<function> ---> %Container<result>

This lets us do functorial things like

but now with a much larger number of arguments, reuse partially applied functions, and run effects with the function container as well as the data container.

Examples

iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3])
[2, 3, 4, 10, 20, 30]

iex> import Witchcraft.Functor
...> [100, 200]
...> ~> fn(x, y, z) ->
...>   x * y / z
...> end <<~ [5, 2]
...>     <<~ [100, 50]
[5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]
# ↓                          ↓
# 100 * 5 / 100          200 * 5 / 50

%Algae.Maybe.Just{just: 42}
~> fn(x, y, z) ->
  x * y / z
end <<~ %Algae.Maybe.Nothing{}
    <<~ %Algae.Maybe.Just{just: 99}
#=> %Algae.Maybe.Nothing{}

Type Class

An instance of Witchcraft.Apply must also implement Witchcraft.Functor, and define Witchcraft.Apply.ap/2.

Functor  [map/2]
   ↓
 Apply   [ap/2]

Link to this section Summary

Functions

Operator alias for ap/2

Apply argumnets to a function, when both are wrapped in the same data structure

Same as ap/2, but with all functions curried

Same as pipe_ap/2, but with all functions curried

Sequence actions, replacing the last argument with the first argument’s values

Extends Functor.lift/2 to apply arguments to a binary function

Extends lift to apply arguments to a ternary function

Extends lift to apply arguments to a quaternary function

Pipe-ordered application. NOT just a flipped version of ap/2

Sequence actions, replacing the first/previous values with the last argument

Operator alias for pipe_ap/2, moving in the pipe direction

Link to this section Types

Link to this section Functions

Link to this function wrapped_funs <<~ wrapped View Source

Operator alias for ap/2

Moves against the pipe direction, but in the order of normal function application

Examples

iex> [fn x -> x + 1 end, fn y -> y * 10 end] <<~ [1, 2, 3]
[2, 3, 4, 10, 20, 30]

iex> import Witchcraft.Functor
...>
...> [100, 200]
...> ~> fn(x, y, z) -> x * y / z
...> end <<~ [5, 2]
...>     <<~ [100, 50]
...> ~> fn x -> x + 1 end
[6.0, 11.0, 3.0, 5.0, 11.0, 21.0, 5.0, 9.0]

iex> import Witchcraft.Functor, only: [<~: 2]
...> fn(a, b, c, d) -> a * b - c + d end <~ [1, 2] <<~ [3, 4] <<~ [5, 6] <<~ [7, 8]
[5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10]

Apply argumnets to a function, when both are wrapped in the same data structure

Examples

iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3])
[2, 3, 4, 10, 20, 30]

iex> [100, 200]
...> |> Witchcraft.Functor.lift(fn(x, y, z) -> x * y / z end)
...> |> ap([5, 2])
...> |> ap([100, 50])
[5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]
# ↓                          ↓
# 100 * 5 / 100          200 * 5 / 50

Same as ap/2, but with all functions curried

Examples

iex> [&+/2, &*/2]
...> |> curried_ap([1, 2, 3])
...> |> ap([4, 5, 6])
[5, 6, 7, 6, 7, 8, 7, 8, 9, 4, 5, 6, 8, 10, 12, 12, 15, 18]
Link to this function curried_pipe_ap(wrapped_args, wrapped_funs) View Source

Same as pipe_ap/2, but with all functions curried

Examples

iex> [1, 2, 3]
...> |> curried_pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end])
[2, 3, 4, 10, 20, 30]

Sequence actions, replacing the last argument with the first argument’s values

This is essentially a sequence of actions forgetting the second argument

Examples

iex> [1, 2, 3]
...> |> following([3, 4, 5])
...> |> following([5, 6, 7])
[
  1, 1, 1, 1, 1, 1, 1, 1, 1,
  2, 2, 2, 2, 2, 2, 2, 2, 2,
  3, 3, 3, 3, 3, 3, 3, 3, 3
]

iex> {1, 2, 3} |> following({4, 5, 6}) |> following({7, 8, 9})
{12, 15, 3}

Extends Functor.lift/2 to apply arguments to a binary function

Examples

iex> lift([1, 2], [3, 4], &+/2)
[4, 5, 5, 6]

iex> [1, 2]
...> |> lift([3, 4], &*/2)
[3, 4, 6, 8]

Extends lift to apply arguments to a ternary function

Examples

iex> lift([1, 2], [3, 4], [5, 6], fn(a, b, c) -> a * b - c end)
[-2, -3, -1, -2, 1, 0, 3, 2]

Extends lift to apply arguments to a quaternary function

Examples

iex> lift([1, 2], [3, 4], [5, 6], [7, 8], fn(a, b, c, d) -> a * b - c + d end)
[5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10]
Link to this function pipe_ap(wrapped, wrapped_funs) View Source
pipe_ap(Witchcraft.Apply.t, Applt.fun) :: Witchcraft.Apply.t

Pipe-ordered application. NOT just a flipped version of ap/2.

This isn’t just a flipped ap in order to get correct effect sequencing.

Examples

iex> [1, 2, 3]
...> |> pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end])
[2, 10, 3, 20, 4, 30]

Sequence actions, replacing the first/previous values with the last argument

This is essentially a sequence of actions forgetting the first argument

Examples

iex> [1, 2, 3]
...> |> then([4, 5, 6])
...> |> then([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,
  7, 8, 9
]

iex> {1, 2, 3} |> then({4, 5, 6}) |> then({7, 8, 9})
{12, 15, 9}
Link to this function wrapped ~>> wrapped_funs View Source

Operator alias for pipe_ap/2, moving in the pipe direction

Examples

iex> [1, 2, 3] ~>> [fn x -> x + 1 end, fn y -> y * 10 end]
[2, 3, 4, 10, 20, 30]

iex> import Witchcraft.Functor
...>
...> [100, 50]
...> ~>> ([5, 2]     # Note the bracket
...> ~>> ([100, 200] # on both `Apply` lines
...> ~> fn(x, y, z) -> x * y / z end))
[5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]