summaryrefslogtreecommitdiff
path: root/lec10/notes.md
blob: e65dbe9dfd21668d00c3563ef46ca2b75af6313e (plain)
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)