mappend
or liftA4
, which seem perfectly natural when viewed in the math lab, but hold the clarity of an indecisive Darth Vader at the drive thru in any other context.Container
s.Container
with a partially applied function inside. More specifically, we have a Container(add(2))
and we'd like to apply its add(2)
to the 3
in Container(3)
to complete the call. In other words, we'd like to apply one functor to another.chain
and then map
the partially applied add(2)
like so:Container(3)
merely to satisfy the monad's sequential demands.ap
is a function that can apply the function contents of one functor to the value contents of another. Say that five times fast.Container(3)
as it's been set free from the jail of the nested monadic function. It's worth mentioning again that add
, in this case, gets partially applied during the first map
so this only works when add
is curried.ap
like so:this.$value
will be a function and we'll be accepting another functor so we need only map
it. And with that we have our interface definition:An applicative functor is a pointed functor with anap
method
ap
character will prove useful. Before we get into it, let's explore a nice property.f
is equivalent to ap
ing a functor of f
. Or in properer English, we can place x
into our container and map(f)
OR we can lift both f
and x
into our container and ap
them. This allows us to write in a left-to-right fashion:of
, each value gets transported to the magical land of containers, this parallel universe where each application can be async or null or what have you and ap
will apply functions within this fantastical place. It's like building a ship in a bottle.Task
in our example. This is a prime situation where applicative functors pull their weight. Let's look at a more in-depth example.Http
calls will happen instantly and renderPage
will be called when both are resolved. Contrast this with the monadic version where one Task
must finish before the next fires off. Since we don't need the destinations to retrieve events, we are free from sequential evaluation.renderPage
is curried or it will not wait for both Tasks
to finish. Incidentally, if you've ever had to do such a thing manually, you'll appreciate the astonishing simplicity of this interface. This is the kind of beautiful code that takes us one step closer to the singularity.signIn
is a curried function of 3 arguments so we have to ap
accordingly. With each ap
, signIn
receives one more argument until it is complete and runs. We can continue this pattern with as many arguments as necessary. Another thing to note is that two arguments end up naturally in IO
whereas the last one needs a little help from of
to lift it into IO
since ap
expects the function and all its arguments to be in the same type.map
is equal to of/ap
, we can write generic functions that will ap
as many times as we specify:liftA2
is a strange name. It sounds like one of the finicky freight elevators in a rundown factory or a vanity plate for a cheap limo company. Once enlightened, however, it's self explanatory: lift these pieces into the applicative functor world.liftA(N)
itself, so it cannot vary in argument length.createUser
takes two arguments, we use the corresponding liftA2
. The two statements are equivalent, but the liftA2
version has no mention of Either
. This makes it more generic and flexible since we are no longer married to a specific type.<gt;
is map
(aka fmap
) and <*>
is just ap
. This allows for a more natural function application style and can help remove some parenthesis.of/ap
is equivalent to map
. We can use this knowledge to define map
for free:chain
, we get functor and applicative for free:ap
's appeal is the ability to run things concurrently so defining it via chain
is missing out on that optimization. Despite that, it's good to have an immediate working interface while one works out the best possible implementation.ap
will never change container types on us (yet another reason to favor over monads). That's not to say we cannot have multiple different effects - we can stack our types knowing that they will remain the same during the entirety of our application.id
all from within a functor shouldn't alter the value in v
. For example:Identity.of(id)
makes me chuckle at its futility. Anyway, what's interesting is that, as we've already established, of/ap
is the same as map
so this law follows directly from functor identity: map(id) == id
.ap
.map
, chain
, and now ap
functions. In the next chapter, we'll learn how to work better with multiple functors and disassemble them in a principled way.Maybe
and ap
.safeAdd
from exercise_b to use liftA2
instead of ap
.