1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
# Lecture 10: Applicative functors (part 1)
## Motivation
Let's suppose that we have the following `Employee` type with a single constructor:
type Name = String
data Employee = Employee { name :: Name, phone :: String }
deriving Show
The constructor for `Employee` is going to be a function with the following signature:
Employee :: Name -> String -> Employee
However, we might want to use this constructor in contexts where we don't know all fields, like in forms with optionally compilable parts. In that case, we can write a new function that turns the `Employee` constructor into something that returns a `Just Employee` only when both its fields have values, or `Nothing` otherwise.
maybeEmployee :: (Name -> String -> Employee) -> (Maybe Name -> Maybe String -> Maybe Employee)
What if we need to extract the fields of an `Employee` object from somewhere else, like a very large data structure or a remote database?
extractedEmployee :: (Name -> String -> Employee) -> ((e -> Name) -> (e -> String) -> (e -> Employee))
We can write this function, too! As it turns out, there is only one way to write it.
## Generalizing
If we extract the common parts of this pattern and make it generic to any type, we should end up with something like this:
(a -> b -> c) -> (f a -> f b -> f c)
This type looks familiar. In fact, it's just the type of `fmap` from the `Functor` class with an extra argument in the two functions! It would be nice if only we could write this new function, which we're going to call `fmap2`, in terms of `fmap`. We might think this is possible, considering that Haskell allows us to partially apply functions.
However, despite our best effort, we actually cannot write `fmap2` in terms of `fmap`. The reason is that, by applying `fmap` to the arguments below one by one, we end up with the type below, which is far from our desired `fmap2`.
h :: a -> (b -> c)
fa :: f a
fb :: f b
fmap h fa :: f (b -> c)
Our applied version of `fmap` lets us apply functions to values in a Functor context. However, we need our functions to be in the Functor context as well! The kind of Functor we need are called **applicative functors**, defined as follows and available in the standard library as `Control.Applicative`.
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
`(<*>)` is pronounced "ap" or "apply" and represents our "contextual application". `pure`, on the other hand, "injects" a value of a type into a container. An interesting aspect of `pure` is that, while our `fmap2` is `fmap` with one more argument, `pure` is `fmap` with one *less* argument. Notice that, since Applicative requires its instances to be instances of Functor as well, we can always use `fmap` with instances of Applicative.
Using `(<*>)` we can now implement `fmap2`, which is also available in the standard library as `liftA2`. The standard library also defines a synonym for `fmap`, `(<$>)`, available in `Control.Applicative`. As a result, `liftA2` is simply written as:
liftA2 :: Applicative f => (a -> b -> c -> f a -> f b -> f c
liftA2 h fa fb = h <$> fa <*> fb
We can write `liftA3` similarly, with the same operators:
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 h fa fb fc = ((h <$> fa) <*> fb) <*> fc
Note how going from `fmap` to `liftA2` required to introduce Applicative, while going from `liftA2` to `liftA3` didn't require further generalization.
On the other hand, `pure` is useful when we need to apply a function to its arguments in the context of some functor `f`, with one or more arguments not being in `f`. Those arguments are often called "pure". By using `pure`, we can lift the arguments not in `f` before applying.
## Applicative laws
An interesting law is the following:
f `fmap` x === pure f <*> x
In other words, applying a function `f` to a container `x` gives the same result as first injecting the function into the container and then applying it to `x` wih `<*>`.
## Applicative examples
An instance of `Applicative Maybe` could be written like this:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
_ <*> Nothing = Nothing
Just f <*> Just x = Just (f x)
|