Witchcraft v1.0.0-beta.2 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
- continue applying values to a curried function resulting from a
Witchcraft.Functor.lift/2
- apply multiple functions to multiple arguments (with lists)
- propogate some state (like
Nothing
inAlgae.Maybe
)
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
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
curried_ap(Witchcraft.Apply.fun, Witchcraft.Apply.t) :: Witchcraft.Apply.t
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]
curried_pipe_ap(Witchcraft.Apply.t, Witchcraft.Apply.fun) :: Witchcraft.Apply.t
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]
following(Witchcraft.Apply.t, Witchcraft.Apply.t) :: Witchcraft.Apply.t
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}
lift(Witchcraft.Apply.t, Witchcraft.Apply.t, (... -> any)) :: Witchcraft.Apply.t
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]
lift(Witchcraft.Apply.t, Witchcraft.Apply.t, Witchcraft.Apply.t, (... -> any)) :: Witchcraft.Apply.t
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]
lift(Witchcraft.Apply.t, Witchcraft.Apply.t, Witchcraft.Apply.t, Witchcraft.Apply.t, (... -> any)) :: Witchcraft.Apply.t
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]
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}
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]