Applicative

Why we need Applicative?

Its common use case could be the following. We have several values with contexts and wanting to combine them with some function (that is also shipped with some context).

For example, we are developping some database, that requires us to construct user if and only if all fields are valid.

Suppose we have a User type whose constructor requires Age, Name field, and two field validators, checkName, checkAge which produces Maybe Age, Maybe Name. It’s obvious that if any of the required field returns Nothing, User should not be constructed. So we’re left the problem: how to combine Age -> Name -> User with Maybe Age, Maybe Name.

Difference between Monad. The combination here must have no dependencies. If so, we should use Monad to handle dependencies.

Applicative is indeed a functor with application, which (minimally) supports

  1. embed pure expressions (pure)
  2. sequence computation and result combination (<*> or liftA2)

It’s like applying functions in the certain context.

Methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- `pure` function just puts a value in the context
pure :: a -> f a

-- Takes a unary function out of context,
-- and then applies it on the extracted value
(<*>) :: f (a -> b) -> f a -> f b

-- Similar to `<*>`, but the function it take is deterministic and binary
liftA2 :: (a -> b -> c) -> f a -> f b -> f c

-- Sequential execution, but discard first arg
(*>) :: f a -> f b -> f b

-- Sequential execution, but discard second arg
(<*) :: f a -> f b -> f b

The most common pattern for Applicative is actually

Common Pattern
1
NormalFunction <$> Context1 <*> Context2 <*> Context3 -- ...

Applicative Laws

  1. Identity
Identity
1
2
-- v :: f a
pure id <*> v = v
  1. Composition
Composition
1
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
  1. Homomorphism.
1
pure f <*> pure x = pure (f x)
  1. Interchange
1
2
-- g :: f (a -> b)
g <*> (pure y) = pure ($ y) <*> g