-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy path14-Applicatives.tex
536 lines (453 loc) · 26.9 KB
/
14-Applicatives.tex
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
\documentclass[DaoFP]{subfiles}
\begin{document}
\setcounter{chapter}{13}
\chapter{Applicative Functors}
Lao Tzu would say, ``If you execute two things in parallel, it will take half the time.''
\section{Parallel composition}
When we translate from the language of category theory to the language of programming, we think of arrows as computations. But computations take time, and this introduces a new temporal dimension. This is reflected in the language we use. For instance, the result of the composition of two arrows, $g \circ f$, is often pronounced $g$ \emph{after} $f$. The execution of $g$ has to \emph{follow} the execution of $f$ because we need the result of $f$ before we can call $g$. We call it \index{sequential composition}\emph{sequential composition}. It reflects the dependency between computations.
There are, however, computations that can be, at least in principle, executed in parallel because there is no dependency between them. This only makes sense if the results of parallel computations can be combined, which means we need to work in a monoidal category. Thus the basic building block of parallel composition is the tensor product of two arrows, $f \colon x \to a$ and $g \colon y \to b$:
\[ x \otimes y \xrightarrow{f \otimes g} a \otimes b \]
In a cartesian category, we'll often use the cartesian product as the tensor. In programming, we'll just run two functions and then combine their results.
\subsection{Monoidal functors}
The problem arises when we try to combine the results of \emph{effectful} computations. For that we need a way of combining a pair of results \hask{f a} and \hask{f b} together with their effects to produce a single result \hask{f (a, b)}. Of course each type of effects requires a different kind of processing.
Let's take the example of the partiality effect. We can combine two such effects using:
\begin{haskell}
combine :: Maybe a -> Maybe b -> Maybe (a, b)
combine (Just a) (Just b) = Just (a, b)
combine _ _ = Nothing
\end{haskell}
The result is a success only if both computations were successful.
A computation should also be able to produce an ``ignore me'' result. This would be a computation that returns a unit value (that is the unit with respect to the cartesian product) and an ``ignore me'' side effect. Notice that returning \hask{Nothing} wouldn't work, because it would have the effect of invalidating the result of the other computation. The correct implementation is:
\begin{haskell}
ignoreMe :: Maybe ()
ignoreMe = Just ()
\end{haskell}
which, when \hask{combine}'d with any other partial computation is ignored (up to left/right unit law).
In general, a type constructor \hask{f} supports parallel composition if it's an instance of the \hask{Monoidal} class:
\begin{haskell}
class Monoidal f where
unit :: f ()
(>*<) :: f a -> f b -> f (a, b)
\end{haskell}
In particular, the \hask{Maybe} functor is \hask{Monoidal}:
\begin{haskell}
instance Monoidal Maybe where
unit = Just ()
Just a >*< Just b = Just (a, b)
_ >*< _ = Nothing
\end{haskell}
Monoidal functors must be compatible with the monoidal structure of the cartesian category, hence they should satisfy the obvious unit and associativity laws.
\subsection{Applicative functors}
Although the class \hask{Monoidal} provides adequate support for parallel composition of effects, it's not very practical.
To begin with, we need be able to operate on the results of composition, thus we need a \hask{Functor} instance for \hask{f}. This lets us apply a function of the type \hask{(a, b)->c } to an effectful pair \hask{f(a, b)}, without touching the effects.
Once we know how to run two computations in parallel, we can compose an arbitrary number of computations by taking advantage of associativity. For instance:
\begin{haskell}
run3 :: (Functor f, Monoidal f) =>
(x -> f a) -> (y -> f b) -> (z -> f c) ->
(x, y, z) -> f (a, b, c)
run3 f1 f2 f3 (x, y, z) =
let fab = f1 x >*< f2 y
fabc = fab >*< f3 z
in fmap reassoc fabc
\end{haskell}
where we re-associate the triple of results:
\begin{haskell}
reassoc :: ((a, b), c) -> (a, b, c)
reassoc ((a, b), c) = (a, b, c)
\end{haskell}
The \index{\hask{let}}\hask{let} clause is used for introducing local bindings. Here, the local variables \hask{fab} and \hask{fabc} are initialized to the corresponding monoidal products. The \hask{let}/\hask{in} construct is an expression whose value is given by the content of the \hask{in} clause.
But as we increase the number of computations, things get progressively more awkward.
Fortunately, there is a more ergonomic approach. In most applications, the results of parallel computations are fed into a function that gathers them before producing the final result. What we need is a way of lifting such a function of multiple arguments---a generalization of the lifting of a single-argument function performed by a \hask{Functor}.
For instance, to combine the results of two parallel computations, we would use the function:
\begin{haskell}
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
\end{haskell}
Unfortunately, we would need infinitely many such functions, for all possible counts of arguments. The trick is to replace them with one function that takes advantage of currying.
A function of two arguments is a function of one argument returning a function. So, assuming that \hask{f} is a functor, we can start by \hask{fmap}'ping:
\begin{haskell}
a -> (b -> c)
\end{haskell}
over the first argument \hask{(f a)} to get:
\begin{haskell}
f (b -> c)
\end{haskell}
Now we need to apply \hask{f (b -> c)} to the second argument \hask{(f b)}. If the functor is \hask{Monoidal}, we can combine the two using the operator \hask{>*< } and get something of the type:
\begin{haskell}
f (b -> c, b)
\end{haskell}
We can then \hask{fmap} the function application \hask{apply} over it:
\begin{haskell}
fmap apply (ff >*< fa)
\end{haskell}
where:
\begin{haskell}
apply :: (a -> b, a) -> b
apply (f, a) = f a
\end{haskell}
Alternatively, we can define the infix operator \hask{<*>} that lets us apply a function to an argument while combining the effects:
\begin{haskell}
(<*>) :: f (a -> b) -> f a -> f b
fs <*> as = fmap apply (fs >*< as)
\end{haskell}
This operator is sometimes called the \index{\hask{<*>}}\index{splat}``splat.''
Conversely, we can implement the monoidal operator in terms of splat:
\begin{haskell}
fa >*< fb = fmap (,) fa <*> fb
\end{haskell}
where we used the pair constructor \hask{(,)} as a two-argument function.
The advantage of using \hask{<*>} instead of \hask{>*<} is that it lets us apply a function of any number or arguments by repeatedly peeling off currying levels.
For instance, we can apply a function of three arguments \hask{g :: a -> b -> c -> d } to three values \hask{fa :: f a }, \hask{fb :: f b }, and \hask{fc :: f c } using:
\begin{haskell}
fmap g fa <*> fb <*> fc
\end{haskell}
We can even use the infix version \index{\hask{<$>}}\hask{<$>} of \hask{fmap} to make it look more like a function application:
\begin{haskell}
g <$> fa <*> fb <*> fc
\end{haskell}
where:
\begin{haskell}
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap
\end{haskell}
Both operators bind to the left, so we can look at this notation as a straightforward generalization of function application:
\begin{haskell}
g a b c
\end{haskell}
except that it also accumulates the effects of the three computations.
To complete the picture, we also need to define what it means to apply an effectful zero-argument function. It just means slapping an ``ignore-me'' effect on top of a single value. We can use the monoidal \hask{unit} to accomplish this:
\begin{haskell}
pure :: a -> f a
pure a = fmap (const a) unit
\end{haskell}
Conversely, \hask{unit} can be implemented in terms of \hask{pure}:
\begin{haskell}
unit = pure ()
\end{haskell}
Thus, in a cartesian closed category, instead of using \hask{Monoidal}, we can use the equivalent, but more convenient \hask{Applicative}:
\begin{haskell}
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
\end{haskell}
\section{Applicative Instances}
The most common applicative functors are also monads (the most notable counterexample being the \hask{ZipList}). Often the choice between using the \hask{Applicative} or the \hask{Monad} syntax is the convenience, although in some cases there is a difference in performance---especially if parallel execution is involved.
\subsection{Partiality}
A \hask{Maybe} function can only be applied to a \hask{Maybe} argument if both are \hask{Just}:
\begin{haskell}
instance Applicative Maybe where
pure = Just
mf <*> ma =
case (mf, ma) of
(Just f, Just a) -> Just (f a)
_ -> Nothing
\end{haskell}
We can add information about the reason for failure by defining a version of the \hask{Either} functor:
\begin{haskell}
data Validation e a = Failure e | Success a
\end{haskell}
The \hask{Applicative} instance for \hask{Validation} accumulates errors using \hask{mappend}:
\begin{haskell}
instance Monoid e => Applicative (Validation e) where
pure = Success
Failure e1 <*> Failure e2 = Failure (mappend e1 e2)
Failure e <*> Success _ = Failure e
Success _ <*> Failure e = Failure e
Success f <*> Success x = Success (f x)
\end{haskell}
\subsection{Logging}
\begin{haskell}
newtype Writer w a = Writer { runWriter :: (a, w) }
deriving Functor
\end{haskell}
For a \hask{Writer} functor to support composition we have to be able to combine the logged values, and to have an ``ignore-me'' element. This means the log has to be a monoid:
\begin{haskell}
instance Monoid w => Applicative (Writer w) where
pure a = Writer (a, mempty)
wf <*> wa = let (f, w) = runWriter wf
(a, w') = runWriter wa
in Writer (f a, mappend w w')
\end{haskell}
\subsection{Environment}
\begin{haskell}
newtype Reader e a = Reader { runReader :: e -> a }
deriving Functor
\end{haskell}
Since the environment is immutable, we pass in parallel to both computations. The no-effect \hask{pure} ignores the environment.
\begin{haskell}
instance Applicative (Reader e) where
pure a = Reader (const a)
rf <*> ra = Reader (\e -> (runReader rf e) (runReader ra e))
\end{haskell}
\subsection{State}
\begin{haskell}
newtype State s a = State { runState :: s -> (a, s) }
deriving Functor
\end{haskell}
The \hask{State} applicative exhibits both parallel and sequential composition. The two actions are created in parallel, but they are executed in sequence. The second action uses the state that's modified by the first action:
\begin{haskell}
instance Applicative (State s) where
pure a = State (\s -> (a, s))
sf <*> sa = State (\s ->
let (f, s') = runState sf s
(a, s'') = runState sa s'
in (f a, s''))
\end{haskell}
\subsection{Nondeterminism}
There are two separate instances of \hask{Applicative} for the list functor. They correspond to two different ways of composing list. The first one zips the two lists element by element; the second produces all possible combinations. In order to have two instances for the same data type, we have to encapsulate one of them in a \hask{newtype}:
\begin{haskell}
newtype ZipList a = ZipList { unZip :: [a] }
deriving Functor
\end{haskell}
The splat operator applies each function to its corresponding argument. It stops at the end of the shorter list. Interestingly, if we want \hask{pure} to act in accord with the unit laws, it has to produce an infinite list:
\begin{haskell}
repeat :: a -> [a]
repeat a = a : repeat a
\end{haskell}
This way, when zipped with any finite list, it will not truncate the result.
\begin{haskell}
instance Applicative ZipList where
pure a = ZipList (repeat a)
zf <*> za = ZipList (zipWith ($) (unZip zf) (unZip za))
\end{haskell}
The two (possibly infinite) lists are combined by applying the function-application operator \hask{$}.
\begin{haskell}
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f [] _ = []
zipWith f _ [] = []
zipWith f (a : as) (b : bs) = f a b : zipWith f as bs
\end{haskell}
The second \hask{Applicative} instance for lists applies all functions to all arguments. To implement the splat operator, we use the Haskell \index{list comprehension}list comprehension syntax:
\begin{haskell}
instance Applicative [] where
pure a = [a]
fs <*> as = [ f a | f <- fs, a <- as ]
\end{haskell}
The meaning of \hask{[f a | f <- fs, a <- as]} is: Create a list of \hask{(f a)} where \hask{f} is taken from the list \hask{fs} and \hask{a} is taken from the list \hask{as}.
\subsection{Continuation}
\begin{haskell}
newtype Cont r a = Cont { runCont :: (a -> r) -> r }
deriving Functor
\end{haskell}
Let's implement the \hask{<*>} operator for continuations. Acting on a pair of continuations
\begin{haskell}
kf :: Cont r (a -> b)
ka :: Cont r a
\end{haskell}
it should produce a continuation:
\begin{haskell}
Cont r b
\end{haskell}
The latter takes a handler \hask{k :: b -> r } and is supposed to call it with the result of function application \hask{(f a)}. To make this last call we need to extract both the function and the argument. To extract \hask{a}, we have to run the continuation \hask{ka} and pass it the consumer of \hask{a}'s:
\begin{haskell}
runCont ka (\a -> k (f a))))
\end{haskell}
To extract \hask{f}, we have to run the continuation \hask{kf} and pass it the consumer of \hask{f}'s:
\begin{haskell}
runCont kf (\f -> runCont ka (\a -> k (f a))))
\end{haskell}
Altogether we get:
\begin{haskell}
instance Applicative (Cont r) where
pure a = Cont (\k -> k a)
kf <*> ka = Cont (\k ->
runCont kf (\f ->
runCont ka (\a -> k (f a))))
\end{haskell}
\subsection{Input/Output}
Input/Output operations are usually composed as a monad, but it's also possible to use them with the applicative syntax. Notice that, even though applicatives compose in parallel, the side effects are serialized. This is important, for instance, if you want the prompt to be printed before taking user input:
\begin{haskell}
prompt :: String -> IO String
prompt str = putStrLn str *> getLine
\end{haskell}
Here we are composing \hask{putStrLn}, which prints its argument to the terminal, and \hask{getLine}, which waits for the user input. The half-splat operator ignores the value produced by the first argument (which, in this case, is the unit \hask{()}), but keeps the side effect (here, printing the string):
\begin{haskell}
(*>) :: Applicative f => f a -> f b -> f b
u *> v = (\ _ x -> x) <$> u <*> v
\end{haskell}
We can now apply a two-argument function:
\begin{haskell}
greeting :: String -> String -> String
greeting s1 s2 = "Hi " ++ s1 ++ " " ++ s2 ++ "!"
\end{haskell}
to a pair of \hask{IO} arguments:
\begin{haskell}
getNamesAndGreet :: IO String
getNamesAndGreet =
greeting <$> prompt "First name: " <*> prompt "Last name: "
\end{haskell}
\subsection{Parsers}
Parsing is a highly decomposable activity. There are many domain specific languages that can be parsed using applicative parsers (as opposed to more powerful monadic parsers).
A parser can be described as a function that takes a string of characters (or tokens). The parsing can fail, so the result is a \hask{Maybe}. A successful parser returns the parsed value together with the unconsumed part of the input string:
\begin{haskell}
newtype Parser a =
Parser { parse :: String -> Maybe (a, String) }
\end{haskell}
For instance, here's a simple parser that detects a digit:
\begin{haskell}
digit :: Parser Char
digit = Parser (\s -> case s of
(c:cs) | isDigit c -> Just (c, cs)
_ -> Nothing)
\end{haskell}
The \hask{Applicative} instance for the parser combines partiality with state:
\begin{haskell}
instance Applicative Parser where
pure a = Parser (\s -> Just (a, s))
pf <*> pa = Parser (\s ->
case parse pf s of
Nothing -> Nothing
Just (f, s') -> case parse pa s' of
Nothing -> Nothing
Just (a, s'') -> Just (f a, s''))
\end{haskell}
Most grammars contain alternatives, for instance, an identifier or a number; an if statement or a loop, etc. This can be captured by the following \index{\hask{Alternative}}subclass of \hask{Applicative}:
\begin{haskell}
class Applicative f => Alternative f where
empty :: f a
(<|>) :: f a -> f a -> f a
\end{haskell}
This says that, no matter what the type \hask{a} is, the type \hask{(f a)} is a monoid.
The \hask{Alternative} instance for a \hask{Parser} tries the first parser and, if it fails, tries the second one. The monoidal unit is a parser that always fails.
\begin{haskell}
instance Alternative Parser where
empty = Parser (\s -> Nothing)
pa <|> pb = Parser (\s ->
case parse pa s of
Just as -> Just as
Nothing -> parse pb s)
\end{haskell}
This can be simplified if we use the \hask{Alternative} instance for \hask{Maybe}:
\begin{haskell}
pa <|> pb = Parser (\s -> parse pa s <|> parse pb s)
\end{haskell}
Having the \hask{Applicative} superclass allows us to chain the alternatives. We can use \hask{some} to parse one or more items:
\begin{haskell}
some :: f a -> f [a]
some pa = (:) <$> pa <*> many pa
\end{haskell}
and \hask{many} to parse zero or more items:
\begin{haskell}
many :: f a -> f [a]
many pa = some pa <|> pure []
\end{haskell}
\begin{exercise}
Implement the \hask{Alternative} instance for \hask{Maybe}. Hint: If the first argument is a failure, try the second one.
\end{exercise}
\subsection{Concurrency and Parallelism}
Starting a thread is an I/O operation, so any concurrent or parallel execution is intrinsically effectful. Whether computations are done in parallel or in series depends on how they are composed. Parallelism is implemented using applicative composition.
An example of an \hask{Applicative} for parallel operations is the functor \hask{Concurrently}. It takes an \hask{IO} action and starts executing it in a separate thread. Applicative composition is then used to combine the results---here, by adding two integers:
\begin{haskell}
f :: Concurrently Int
f = (+) <$> Concurrently (fileChars "1-Types.hs")
<*> Concurrently (fileChars "2-Composition.hs")
where fileChars path = length <$> readFile path
\end{haskell}
\subsection{Do Notation}
In category theory we compose arrows diagrammatically. We can either join them in series or in parallel, and there is no need to give names the elements of intermediate objects. Such diagrams can be translated directly to programming, resulting in point-free notation for serial composition, and applicative notation for parallel composition. However, it's often convenient to name intermediate results. This can be done using the \hask{do} notation. For instance the concurrency example can be rewritten as:
\begin{haskell}
{-# language ApplicativeDo #-}
f :: Concurrently Int
f = do
m <- Concurrently (fileChars "1-Types.hs")
n <- Concurrently (fileChars "2-Composition.hs")
pure (m + n)
where fileChars path = length <$> readFile path
\end{haskell}
We'll talk more about the do notation in the chapter on monads. For now, it's important to know that, when you use the language pragma \hask{ApplicativeDo}, the compiler will by default will try to use applicative composition in the translation of do blocks. If that's impossible, it will fall back on monadic composition.
\subsection{Composition of Applicatives}
A nice property of applicative functors is that their functor composition is again an applicative:
\begin{haskell}
instance (Applicative f, Applicative g) =>
Applicative (Compose g f) where
pure :: a -> Compose g f a
pure x = Compose (pure (pure x))
(<*>) :: Compose g f (a -> b) -> Compose g f a -> Compose g f b
Compose gff <*> Compose gfa = Compose (fmap (<*>) gff <*> gfa)
\end{haskell}
We'll see later that the same is not true for monads: a composition of two monads is, in general, not a monad.
\section{Monoidal Functors Categorically}
We've seen several examples of monoidal categories. Such categories are equipped with some kind of binary operation, e.g., a cartesian product, a sum, composition (in the category of endofunctors), etc. They also have a special object that serves as the unit with respect to that binary operation. Unit and associativity laws are satisfied either on the nose (in strict monoidal categories) or up to isomorphism.
Every time we have more than one instance of some structure, we may ask ourselves the question: is there a whole category of such things? In this case: do monoidal categories form their own category? For this to work we would have to define arrows between monoidal categories.
A \emph{monoidal functor} $F$ from a monoidal category $(\mathcal{C}, \otimes, i)$ to another monoidal category $(\mathcal{D}, \oplus, j)$ maps tensor product to tensor product and unit to unit---all up to isomorphism:
\begin{align*}
F a \oplus F b &\cong F (a \otimes b) \\
j &\cong F i
\end{align*}
Here, on the left-hand side we have the tensor product and the unit in the target category, and on the right their counterparts in the source category.
If the two monoidal categories in question are not strict, that is the unit and associativity laws are satisfied only up to isomorphism, there are additional coherency conditions that ensure that unitors are mapped to unitors and associators are mapped to associators.
The category of monoidal categories with monoidal functors as arrows is called $\mathbf{MonCat}$. In fact it's a 2-category, since one can define structure-preserving natural transformations between monoidal functors.
\subsection{Lax monoidal functors}
One of the perks of monoidal categories is that they allow us to define monoids. You can easily convince yourself that monoidal functors map monoids to monoids. It turns out that you don't need the full power of monoidal functors to accomplish that. Let's consider what the minimal requirements are for a functor to map monoids to monoids.
Let's start with a monoid $(m, \mu, \eta)$ in the monoidal category $(\mathcal{C}, \otimes, i)$. Consider a functor $F$ that maps $m$ to $F m$. We want $F m$ to be a monoid in the target monoidal category $(\mathcal{D}, \oplus, j)$. For that we need to find two mappings:
\begin{align*}
\eta' &\colon j \to F m \\
\mu' &\colon F m \oplus F m \to F m
\end{align*}
satisfying monoidal laws.
Since $m$ is a monoid, we do have at our disposal the liftings of the original mappings:
\begin{align*}
F \eta &\colon F i \to F m \\
F \mu &\colon F (m \otimes m) \to F m
\end{align*}
What we are missing, in order to implement $\eta'$ and $\mu'$, are two additional arrows:
\begin{align*}
j &\to F i\\
F m \oplus F m &\to F (m \otimes m)
\end{align*}
A monoidal functor would provide such arrows. However, for what we're trying it accomplish, we don't need these arrows to be invertible.
A \emph{lax monoidal functor} is a functor equipped with a morphism $\phi_i$ and a natural transformation $\phi_{ab}$:
\begin{align*}
\phi_i &\colon j \to F i \\
\phi_{a b} &\colon F a \oplus F b \to F (a \otimes b)
\end{align*}
satisfying the appropriate unitality and associativity conditions.
Such a functor maps a monoid $(m, \mu, \eta)$ to a monoid $(F m, \mu', \eta')$ with:
\begin{align*}
\eta' &= F \eta \circ \phi_i \\
\mu' &= F \mu \circ \phi_{a b}
\end{align*}
In Haskell, we recognize the \hask{Monoidal} functor as an example of a lax monoidal endofunctor that preserves the cartesian product.
\subsection{Functorial strength}
There is another way a functor may interact with monoidal structure, one that hides in plain sight when we do programming. We take it for granted that functions have access to the environment. Such functions are called closures.
For instance, here's a function that captures a variable \hask{a} from the environment and pairs it with its argument:
\begin{haskell}
\x -> (a, x)
\end{haskell}
This definition makes no sense in isolation, but it does when the environment contains the variable \hask{a}, e.g.,
\begin{haskell}
pairWith :: Int -> (String -> (Int, String))
pairWith a = \x -> (a, x)
\end{haskell}
The function returned by calling \hask{pairWith 5} ``closes over'' the 5 from its environment.
Now consider the following modification, which returns a singleton list that contains the closure:
\begin{haskell}
pairWith' :: Int -> [String -> (Int, String)]
pairWith' a = [\x -> (a, x)]
\end{haskell}
As a programmer you'd be very surprised if this didn't work. But what we do here is highly nontrivial: we are smuggling the environment \emph{under} the list functor. According to our model of lambda calculus, a closure is a morphism from the product of the environment and the function argument. Here the lambda, which is really a function of \hask{(Int, String)}, is defined inside a list functor, but it captures the value \hask{a} that is defined \emph{outside} the list.
The property that lets us smuggle the environment under a functor is called \index{strength, functorial}\emph{functorial strength} or \emph{tensorial strength} and can be implemented in Haskell as:
\begin{haskell}
strength :: Functor f => (e, f a) -> f (e, a)
strength (e, as) = fmap (e, ) as
\end{haskell}
The notation \hask{(e, )} is called a \index{tuple section}\emph{tuple section} and is equivalent to the partial application of the pair constructor: \hask{(,) e}.
In category theory, strength for an endofunctor $F$ is defined as a natural transformation that smuggles a tensor product into a functor:
\[ \sigma \colon a \otimes F(b) \to F (a \otimes b) \]
There are some additional conditions which ensure that it works nicely with the unitors and the associator of the monoidal category in question.
The fact that we were able to implement \hask{strength} for an arbitrary functor means that, in Haskell, every functor is strong. This is the reason why we don't have to worry about accessing the environment from inside a functor.
In category theory, though, not every endofunctor in a monoidal category is strong. For now, the magic incantation is that the category we're working with is self-enriched, and every endofunctor defined in Haskell is enriched. We'll come back to it when we talk about enriched categories. In Haskell, strength boils down to the fact that we can always \hask{fmap} a partially applied pair constructor \hask{(a, )}.
\subsection{Closed functors}
If you squint at the definition of the splat operator:
\begin{haskell}
(<*>) :: f (a -> b) -> (f a -> f b)
\end{haskell}
you may see it as mapping function objects to function objects.
This becomes clearer if you consider a functor between two categories, both of them closed. You may start with a function object $b^a$ in the source category and apply the functor $F$ to it:
\[ F (b^a) \]
Alternatively, you may map the two objects $a$ and $b$ and construct a function object between them in the target category:
\[ (F b)^{F a} \]
If we demand that the two ways be isomorphic, we get a definition of a strict \emph{closed functor}. But, as was the case with monoidal functors, we are more interested in the lax version, which is equipped with a one-way natural transformation:
\[ F (b^a) \to (F b)^{F a} \]
If $F$ is an endofunctor, this translates directly into the definition of the splat operator.
The full definition of a lax closed functor includes the mapping of the monoidal unit and some coherence conditions. All said, an applicative functor is a lax closed functor.
\end{document}