Realm v0.1.0 Realm.Apply protocol View Source

An extension of Realm.Functor, Apply provides a way to apply arguments to functions when both are apply in the same kind of container. This can be seen as running function application "in a context". For a nice, illustrated introduction, see Functors, Applicatives, And Monads In Pictures.

Graphically

If function application looks like this

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 Realm.Functor.map/2
  • apply multiple functions to multiple arguments (with lists)
  • propogate some state (like Nothing in Algae.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> [100, 200]
...> |> Realm.Functor.map(curry(fn)(x, y, z) -> x * y / z end)
...> |> provide([5, 2])
...> |> provide([100, 50])
[5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]
# ↓                          ↓
# 100 * 5 / 100          200 * 5 / 50
iex> import Realm.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{}

convey vs ap

convey and ap essentially associate in opposite directions. For example, large data is usually more efficient with ap, and large numbers of functions are usually more efficient with convey. It's also more consistent consistency. In Elixir, we like to think of a "subject" being piped through a series of transformations. This places the function argument as the second argument. In Realm.Functor, this was of little consequence. However, in Apply, we're essentially running superpowered function application. ap is short for apply, as to not conflict with Kernel.apply/2, and is meant to respect a similar API, with the function as the first argument. This also reads nicely when piped, as it becomes [funs] |> ap([args1]) |> ap([args2]), which is similar in structure to fun.(arg2).(arg1). With potentially multiple functions being applied over potentially many arguments, we need to worry about ordering. convey not only flips the order of arguments, but also who is in control of ordering. convey typically runs each function over all arguments (first_fun ⬸ all_args), and ap runs all functions for each element (first_arg ⬸ all_funs). This may change the order of results, and is a feature, not a bug.

iex> [1, 2, 3]
...> |> Realm.Apply.convey([&(&1 + 1), &(&1 * 10)])
[
  2, 10, # [(1 + 1), (1 * 10)]
  3, 20, # [(2 + 1), (2 * 10)]
  4, 30  # [(3 + 1), (3 * 10)]
]
iex> [&(&1 + 1), &(&1 * 10)]
...> |> ap([1, 2, 3])
[
  2,  3,  4, # [(1 + 1),  (2 + 1),  (3 + 1)]
  10, 20, 30 # [(1 * 10), (2 * 10), (3 * 10)]
]

Type Class

An instance of Realm.Apply must also implement Realm.Functor, and define Realm.Apply.convey/2.

Functor  [map/2]
   
 Apply   [convey/2]

Link to this section Summary

Functions

Pipe arguments to functions, when both are apply in the same type of data structure.

Link to this section Types

Link to this section Functions

Link to this function

convey(apply, func)

View Source
convey(t(), t()) :: t()

Pipe arguments to functions, when both are apply in the same type of data structure.

Examples

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