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
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171# FP + Monads
If you're already comfortable with Functional Programming (FP), especially side effects and pure functions and such, you can [skip to "Expansive Intro To Monads"](#expansive-intro-to-monads) below.
----
----
**NOTE: This guide's content is also available in ebook (PDF/EPUB) form for easier reading, as ["Unwrapping Monads & Friends" on Leanpub](https://leanpub.com/monads-and-friends). Download for free (or purchase at suggested price of $2.99 USD).**
----
----
## Functional Programming (FP)
FP is a topic that often carries with it a fair bit of "baggage", and that goes even more for monads. It's quite easy to get lost out in the web-of-google-searches when bombarded by the formalized terminology or math behind these topics, especially since so many FP fans believe that the formalism and math *are basically required* to get anything out of them.
Hear me on this: **you DO NOT need a CS or Math degree** to immerse yourself in FP, and further to adopt a mindset around monads. The formalism and math can offer a richer and deeper experience with the topics the further you dive into them, but you don't have to start there (unless you want to!).
Have you written code like this before?
```js
const FPBookNames = [];
for (const record of data) {
if (record.topic == "FP") {
FPBookNames.push(record.bookName);
}
}
```
This is what we typically call "imperative" style code. It's comfortable and familiar to most of us. But it focuses on *how* to do a task. To understand the *what* or *why* of that code, you have to sort of mentally execute the code and infer its meaning. Only after reading it, you might realize: "that code is selecting records with a topic of 'FP' and sticking their book-name into an array".
What if your code could be more "declarative" and state the *what* and *why* more clearly at a glance, de-emphasizing the *how* as a less important implementation detail? Would being able to determine the purpose of a snippet of code more readily and effectively, make that code *more readable*? What if that code was also more resilient (less susceptible to bugs) and more testable (more isolated/pure)?
That's why FP exists.
### From Loop To Map And Filter
For example, imagine you have a single string value, and you want to uppercase the value.
```js
function uppercase(str) { return str.toUpperCase(); }
var greeting = "Hello, friend!";
console.log( uppercase(greeting) ); // HELLO, FRIEND!
```
That's pretty straightforward. But now let's say you had multiple strings to uppercase. You could manually call `uppercase(..)` for each string. But when we have multiple values, it's often more convenient to stick them in an array. Imperatively, uppercasing an array of strings would likely be done like this:
```js
// assumed: function uppercase(str) { .. }
// assumed: `listOfStrings` (array of string values)
for (let i = 0; i < listOfStrings.length; i++) {
listOfStrings[i] = uppercase(listOfStrings[i]);
}
```
But here we modified the entries in the array by replacing each original string with its uppercase version. In FP, we generally prefer not to modify/reassign but rather to create *new* values, as a way to cut down on the chances of unexpected side-effects causing bugs in the program. So let's do that by creating a new list:
```js
// assumed: function uppercase(str) { .. }
// assumed: `listOfStrings` (array of string values)
let listOfUpperStrings = [];
for (let i = 0; i < listOfStrings.length; i++) {
listOfUpperStrings[i] = uppercase(listOfStrings[i]);
}
```
That code is perfectly *fine*. But the first time you encounter it, to understand the overall "what", you have to mentally execute that code and infer its purpose. Afterwards, you can assert, "this code takes a list of string values and produces a new list of all the values uppercased".
Performing the same operation for each value in a list, is a pretty common task in programming. So much so that we have named this task and invented well known utilities for it, specifically `map(..)`. Let's see it:
```js
// assumed: function uppercase(str) { .. }
// assumed: `listOfStrings` (array of string values)
const listOfUpperStrings = listOfStrings.map(uppercase);
```
The array `map(..)` takes a single function as input. This function needs to receive a single value and return a value back. `uppercase(..)` fits that description, so we pass it directly. `map(..)` gives us back a new array, containing all the return values from calling the provided function (`uppercase(..)`) against the original values.
One general assertion of FP is that the mechanics of looping over a list, and calling a function against each value in the list, are so well known as to not need to be written explicitly in code. Instead, we use `map(..)`. The resulting code is more *declarative* than *imperative*. As such, the reader -- if they know what `map(..)` does, already -- can more readily glance at it and recognize, *without much mental execution*, that the outcome (the "what") of this code is a new list of uppercased strings.
FP has recognized a whole bunch of these common tasks, and named and implemented them as recognized utilities.
For example, `filter(..)` does something similar to `map(..)`, but instead of producing new values, it performs an `if` to decide if a value should be kept/included in the new list or not.
```js
// assumed: `listOfStrings` (array of string values)
function isLongEnough(str) { return str.length > 50; }
const listOfLongStrings = listOfStrings.filter(isLongEnough);
```
`listOfLongStrings` will be a new array that includes only strings from the original `listOfStrings` that are longer than 50 characters. And that outcome should be more readily discernable than if we'd written the `for` loop imperative equivalent.
And we can even "compose" (i.e., do both together) the `map(..)` and `filter(..)` operations:
```js
// assumed: function uppercase(str) { .. }
// assumed: function isLongEnough(str) { .. }
// assumed: `listOfStrings` (array of string values)
listOfStrings.filter(isLongEnough).map(uppercase);
```
Now we have a list of long-enough strings that have all been uppercased!
So that's a bit of the early mindset adoption that getting into FP brings you. It's the tip of a massive iceberg.
### Where To Learn More FP?
If you're intrigued, but new to such FP concepts, I invite you to check out -- at least the first several chapters of -- my free-to-read-online FP book: [Functional-Light JavaScript](https://github.com/getify/functional-light-js).
There are also several [high-quality video courses about FP on Frontend Masters](https://frontendmasters.com/courses/?q=functional), including:
* Bianca Gandolfo's ["From Fundamentals To Functional JS"](https://frontendmasters.com/courses/js-fundamentals-functional-v2/)
* Anjana Vakil's ["Functional JavaScript First Steps"](https://frontendmasters.com/courses/functional-first-steps/)
* My ["Functional-Light JavaScript"](https://frontendmasters.com/courses/functional-javascript-v3/) -- a companion course to [my book](https://github.com/getify/functional-light-js) of the same name
I think your first big goal should be to understand and feel comfortable with -- but not an expert on! -- the following topics:
* Side Effects
* Pure Functions
* Higher-order Functions
* Function Composition
* Currying
* Basic List Operations (filter/map/reduce)
### How Do I Know...?
How might you know if you're on the right path and comfortable enough with FP to move on to monads? There's no great way for me to answer that for all readers of this guide. But I at least want to offer a bit of a glimpse or hint instead of leaving you only with the unsatisfying, "it depends".
There are of course many ways (e.g., with `reduce(..)`) to approach the `FPBookNames` code snippet at the beginning, in FP style. I'm not going to assert that there's "one right way".
But one approach that's somewhat common in FP, which relies on chained expressions and composed (and curried!) functions, goes by the name "point-free style", and could look like this:
```js
const FPBookNames = data
.filter( compose(
eq("FP"),
getProp("topic")
) )
.map( getProp("bookName") );
```
Again, not to say this is the "right" way to do it, but... code like this represents the combination of ideas from FP that I think will help prepare you to take on *monads*, especially as I will present them throughout the rest of this guide.
When code like that *speaks to you*, I think it's time to dip your toes into the ocean of monads.
## Expansive Intro To Monads
In addition to the guide I present here, I recommend checking out a [recording of my conference talk, "Mo'problems, Mo'nads"](https://www.youtube.com/watch?v=bg0Wtz3sR9U).
----
*Monad* is a funny word, let's admit it. It's a mathematical term more than a programming term. It's not even close to what most of us would reach for if we were coming up with a name for a powerful thing we hoped everyone around us would adopt!
Before we get to a more technical definition, let me try to ease you in. It may end up seeming a bit anti-climatic once you've read the next several sections of this guide, because you may be expecting it to be a big, complex topic. And in some ways it is. You may spend months or years revisiting *monads* (and related topics) and building a deeper understanding. I certainly am still learning them, several years on.
But at the outset, I hope you feel somewhat comfortable with the basics of this topic in *just a few more minutes* of reading here. I hope your reaction is, "Oh, is that all a monad is?!"
The formalism, the terminology, the mathematical notation, it can really start to overblow the concepts to an intimidating level. I stayed away from *monads* for a long time. But now I'm hooked. And I hope you get there, too.
### What Is Monad?
Here's the simplest way I know how to describe what a monad is without getting into code. A monad isn't actually a specific *thing* so much as it is a *pattern*. In our programs, we can use *things* that behave according to this pattern (called "Monad", capitalized). These *things* in our programs are often referred to as "monads" (lowercased), but that's a bit informal. Instead, it might be more appropriate to think of those *things* in your program as instances of a *monad type*, kind of like the number `42` is a specific instance (value) of the number type.
The point of the monad pattern is to describe some behaviors we can expect with these instances when we interact with them. It's to give them a predictability, much like we know that the number `39` and the number `3` can be added to make the number `42`.
But it goes beyond that. Monad can perhaps more accurately be described as a pattern for a group of other patterns, like a higher-level type or a meta-type.
**Tip:** A simplified way of illustrating such a higher-level type / meta-type concept: in JS, several different *types* of values (numbers, strings, booleans, etc) all are described as "primitive" values (aka, not the "object" value type). Each individual type has its own respective behaviors, but all primitive values (of any primitive type) also share a common set of behavior; specifically, all JS primitive values held by-value and assigned/passed by-value-copy -- as opposed to object value types being held by-reference and assigned/passed by-reference-copy.
So *Monads* (plural) would refer collectively to a variety of different *types* of monads -- which, again, we can define instances of in our programs -- each of which has their own unique individual pattern (behavior, etc). But all of these different Monads -- aka, "monad types", "monad subtypes", "monad kinds", or however works best for your brain -- *also* conform to the core Monad pattern (with its specific behaviors).
Formally, *Monad* is a part (Type) of a broad mathematical concept called "Category Theory". You could briefly and incompletely describe Category Theory as a way to categorize/group ideas based on how they behave with respect to composition and transformation, including as you mix them with each other.
The Monad type, as it appears in programming, is a way to represent a value or operation that associates the required specific behaviors with/around that (underlying) value/operation. These additional behaviors augment (i.e., improve!) the original value/operation with some "guarantees" about how it will interact predictably with other monad-represented values/operations in the program.
Your head may already be swimming with the abstractiveness of all that. Take a few minutes to let it sink in, then let's move on to illustrating them with some JS code.
### Simplest JS Illustration
What's the most stripped-down way we could implement the Monad type in JS? How about this:
```js
function Identity(v) {
return { val: v };
}
function chain(m,fn) {
return fn(m.val);
}
```
That's it, that's a monad at its most basic. In particular, it's the "Identity" monad, which means that it will merely hold onto a value, and let you use that value, untouched, whenever you want to.
```js
const myAge = Identity(41); // { val: 41 }
```
We put a value inside an object container here only so we could recognize the value as *being monadic* (behaving according the Monad type). This "container"ness is one convenient way of implementing a monad, but it's not actually required.
The `chain(..)` function provides a minimum basic capability to interact with our monad instance. For example, imagine we wanted to take the monadic representation of `41` and produce another monad instance where `41` had been incremented to `42`?
```js
const myAge = Identity(41); // { val: 41 }
const myNextAge = chain( myAge, v => Identity(v + 1) ); // { val: 42 }
```
We have two distinct, concrete values (held in `myAge` and `myNextAge`), both of which are instances of the "Identity" monad (as represented by this `Identity(..)` function and its `chain(..)` function). Again, people often call these instances "monads" (plural), but it's better to think of them as instances of a single type of Monad, "Identity".
It's important to note that even though I use the function names `Identity` and `chain` here, those are simply just artistic choices. There's nothing explicitly required by the concept of *Monad* in terms of what we name these things. But like any good programming discipline, if we use names that others have regularly chosen, it helps create a familiarity that improves our communications.
That `chain(..)` function looks pretty basic, but it's really important (whatever it's called). We'll dig more into it much more in a bit.
But for now, I'm sure that code snippet seems pretty underwhelming to most readers. Why not just stick with `41` and `42` instead of `{ val: 41 }` and `{ val: 42 }`? The *WHY* of monads is likely not at all apparent yet. You'll have to hang with me for a bit to start to uncover the *WHY*.
Hopefully I've at least shown you that down at the very core, a monad is not a mystical or complex *thing*.
Perhaps you just had that "Oh, is that it!?" moment.
### Building Up Monads
Monads have somewhat (in)famously been described with a variety of silly-sounding metaphors, like "burritos". Others call monads "wrappers" or "boxes", or "data structures" or... the truth is, all these ways of describing a monad are partial descriptions. It's like looking at a Rubik's Cube. You can look at one face of the cube, then turn it around and look at a different face, and get more of the whole thing.
A complete understanding requires being familiar with all sides. But complete understanding is not a single atomic event. It's often built up by lots of smaller bits of understanding, like looking at each face of the cube one at a time.
For now, I just want you to focus on the idea that you could take a value like `42`, or an operation like `console.log("Hello, friend!")`, and attach/associate additional behaviors to them which will give them super powers. That's what the *Monad* type/pattern will do.
Here's another possible way of expressing monad instances in JS, using capabilities provided by the **Monio** library:
```js
const myAge = Just(41);
```
**Monio Reference: [`Just`](MONIO.md)**
The above code shows a function called `Just(..)`, which is pretty similar to the `Identity(..)` function shown previously. It acts as a constructor (aka, "unit") of the `Just` monad.
And also...
```js
const printGreeting = IO(() => console.log("Hello, friend!"));
```
Here we see another **Monio** function called `IO(..)`, which acts as a constructor for the `IO` monad (which holds functions).
Thinking of our sketch in the previous section, you could sort of think of `myAge` as `{ val: 41 }` and `printGreeting` as `{ val: () => console.log("Hello, friend!") }`. **Monio**'s representation is actually a bit more sophisticated than just an object like that. But under the covers, it's not *that far* different.
I'm going to use **Monio** throughout the rest of the guide, so that we don't have to keep inventing all our own monad implementations. The convenient affordances are nice to use, and easier to illustrate with.
Keep in mind, however, that under all the trappings, we could be doing something as straight-forward as defining an object like `{ val: 41 }`.
#### Digging Into Map
Instances of `Just(..)` as shown above come with some methods on them, namely `chain(..)` (like we saw earlier) and also `map(..)`, which we'll look at now.
Consider the notion of an array's `map(..)` method. Its job is to apply a mapping (value translation) operation against all the contents of the associated array.
```js
[ 1, 2, 3 ].map(v => v * 2); // [ 2, 4, 6 ]
```
**Note:** the technical term for this capability is Functor. In fact, all monads are Functors, but don't worry too much about that term for now. Just file in the back of your head.
We started with one array (`[ 1, 2, 3 ]`) and produced a new distinct array (`[ 2, 4, 6 ]`) by mapping each element in the original array to a new value that was doubled.
This mapping on arrays of course works even if our array has a single element, right?
```js
[ 41 ].map(v => v + 1); // [ 42 ]
```
An extremely important detail there, that's easy to miss, is that the `map(..)` function didn't just give us `42` (a number) but gave us `[ 42 ]` (array holding a number). Why? Because `map(..)`'s job is to produce a new instance of the same type of "container" it was invoked against. In other words, if you use array's `map(..)`, you're going to always get back an array.
But what if our "container" is a monad instance, and what if there's only one underlying value, like `41` in it? Since the monad is also a functor (able to be "mapped"), we should still expect the same kind of outcome, right?
```js
const myAge = Just(41);
const myNextAge = myAge.map(v => v + 1); // Just(42)
```
Hopefully it makes intuitive sense here that `myNextAge` should be another `Just` instance, representing the underlying number `42`.
Recall this bare-bones example from the previous section?
```js
// assumed: function Identity(val) { .. }
// assumed: myAge ==> { val: 41 }
const myNextAge = chain( myAge, v => Identity(v + 1) ); // { val: 42 }
```
Substituting **Monio**'s implementation, that looks like:
```js
const myNextAge = myAge.chain(v => Just(v + 1));
```
So what's the relationship here between the `map(..)` and `chain(..)`? Let's line the operations up next to each other, to see *it*:
```js
myAge.map( v => v + 1 ); // Just(42)
myAge.chain( v => Just(v + 1) ); // Just(42)
```
Now do you see *it*? `map(..)` assumes that its returned value needs to be automatically "wrapped up" in an instance of the "container", whereas `chain(..)` expects the return value to already be "wrapped up" in the right kind of "container".
The `map(..)` function doesn't at all have to be named that to satisfy the functor'ness of the monad instance. In fact, you don't even strictly *need* a `map(..)` function at all, if you have `chain(..)`, because `map(..)` can be implemented with `chain(..)`:
```js
function JustMap(m,fn) { return m.chain(v => Just(fn(v))); }
fortyOne.map( v => v + 1); // Just(42)
JustMap(fortyOne,v => v + 1); // Just(42)
```
Having `map(..)` (or whatever it's called) available is a convenience over using just the `chain(..)` by itself; but it's not strictly required.
### Monadic Chain
`chain(..)` sometimes goes by other names (in other libraries or languages), like `flatMap(..)` or `bind(..)`. In **Monio**'s monads, all three methods names are aliased to each other, so pick whichever one you prefer.
The name `flatMap(..)` can help reinforce the relationship between it and `map(..)`.
```js
Just(41).map( v => Just(v + 1) ); // Just(Just(42)) -- oops!?
Just(41).flatMap( v => Just(v + 1) ); // Just(42) -- phew!
```
If we return a `Just` monad instance from `map(..)`, it still wraps that in another `Just`, so we end up with nesting. That is perfectly valid and sometimes desired, but often not. If we return the same form of value from `flatMap(..)` (again, aka `chain(..)`), there's no nesting. Essentially, the `flatMap(..)` flattens out the nesting by one level!
The `chain(..)` method is intended for the provided function to return a monad of the same kind (`Just`, `Maybe`, etc) as the one the method was invoked on. However, **Monio** does not generally perform explicit type enforcement, so there's nothing that strictly prevents such crossing of monad kinds (e.g., between `Just` and `Maybe`). It's up to the developer to follow (or not) the implied type characteristics of these mechanisms.
I've asserted `chain(..)` (or whatever we call it!) is pretty central to something being monadic. Yet even as simple as `chain` looks to implement ([see earlier](#simplest-js-illustration)), it works in such a specific way that it provides some very important guarantees about how one monad instance can interact with another monad instance. Such interactions and transformations are critical to building up a program of monads without chaos.
Another side of the **Monad** Rubik's Cube is these guarantees; they're ensured by a set of "laws" that all conforming monad implementations must satisfy:
1. Left Identity
2. Right Identity
3. Associativity
The formality and mathematical importance of these laws is not super important to immerse in right now. But to illustrate them very simply with our trivial identity monad `Just` from **Monio**:
```js
// helpers:
const inc = v => Just(v + 1);
const double = v => Just(v * 2);
// (1) "left identity" law
Just(41).chain(inc); // Just(42)
// (2) "right identity" law
Just(42).chain(Just); // Just(42)
// (3) "associativity" law
Just(20).chain(inc).chain(double); // Just(42)
Just(20).chain(v => inc(v).chain(double)); // Just(42)
```
Here I used **Monio**'s `chain(..)` method; that's again merely for convenient illustration. The monad laws are stated in terms of a *chain* operation, regardless of what an implementation chooses to call it.
### Back To The Core Of Monad
Boiling this all down: the *Monad* type only strictly requires two things:
1. a function (of any name) to construct an "instance" of the type (the unit constructor)
2. a function (of any name) to properly perform the "chain" operation, as shown in the 3 laws
Everything else you see in the code snippets in this guide, such as wrapper monad instances, specific method names, ["friends of monads" behaviors](#-and-friends), etc -- that's all convenient affordance provided specifically by **Monio**.
But from that narrow perspective, a monad doesn't have to be a "container" (like a wrapping object or class instance) and there doesn't even have to be a concrete "value" (like `42`) involved. While a "container wrapping a value" is one potentially helpful side of the Rubik's Cube to look at, it's not *all* that a monad is or can be. Don't get too *wrapped up* in that way of thinking!
### But... How Do I Get Something Out!?
You may still be wondering: how do we ever extract the value (like primitive number `42`) -- or indeed, whatever *thing* the monad is representing -- out/away from a monadic representation? It seems like every monadic operation just produces another monad instance. At some point, we might need the actual number `42` to print to the screen or insert in a database, right!?
One key idea of FP, and especially of monads, is to *defer* the need for the underlying values until the last possible moment. With respect to monads, we prefer to keep everything "lifted" in the monadic space as long as possible.
But yes, sometimes we *do* need to reduce a monad down to a "real" value. There are other ways of accomplishing that outcome, but here's one approach, a preview of what we'll [talk about later in the guide](#-and-friends). To "extract" the value from a monad like the `Just` identity monad, we can use a method **Monio** provides, called `fold(..)`:
```js
const identity = v => v;
const myAge = Just(41);
const myNextAge = myAge.map(birthday);
// later:
const ageIsJustANumber = myNextAge.fold(identity);
console.log(`I'm about to be ${ ageIsJustANumber } years old!`);
// I'm about to be 42 years old!
```
So yes, there's an "escape valve" (`fold(identity)`) where we can exit from our `Just` monad.
But remember: monads play best with other monads ([and their friends!](#-and-friends)), so it's better to stay in that space as much as we can. Let's hold off discarding the monad representation until (unless!) we absolutely have to.
Also keep in mind: `fold(..)` as shown here, and provided on many of **Monio**'s monads, is **NOT** a *Monad* behavior; it comes [from a *friend* called Foldable](#foldable).
### *Maybe* Something More?
**Monio Reference: [`Maybe`](MONIO.md)**
The identity monad `Just` probably doesn't seem all that amazing. It's cute and *maybe* a little clever, but it's kinda unimpressive. In practice, we'll almost never directly create `Just` monad instances.
It's foundational. It's not supposed to seem revolutionary in and of itself, as that would present too tall a cliff to climb from the get-go. If the first numbers you ever learned as a young child were not 2 or 3, but were instead √2 or π, you might have found learning basic math pretty tough in those earliest days!
If `Just` *seems* (too) simple to you, that's probably a good thing! You're likely well on your way to *getting* monads. But don't let its simplicity bore you as there not being anything worth your time; there is!
There are lots of variations/augmentations on top of this basic monad concept that get more interesting. I could spend many hours and many dozens of pages detailing even a sampling of them. But let me continue incrementally by briefly illustrating another example of monads that builds off the identity monad.
You've probably written code like this before in your imperative-style programs:
```js
const shippingLabel = (
(record != null && record.address != null && record.address.shipping != null) ?
formatLabel(record.address.shipping) :
null
);
```
The `!= null` checks that we have to pepper throughout our programs are to avoid JS exceptions when we do operations against these values that expect them to be non-null'ish (`null` or `undefined`).
However, JS recently (ES2020) added an "optional chaining" operator which simplies that type of code a bit:
```js
const shippingLabel = (
(record?.address?.shipping != null) ?
formatLabel(record.address.shipping) :
null
);
```
The `?.` operator (as opposed to bare `.`), right before `address` and `shipping`, is a short-circuiting operator that skips out of further expression evaluation if the preceeding element evaluates as null'ish. That means we don't need the `record != null` or `record.address != null` checks, because the `?.` operator does it for us.
We're protected now from `record` or `record.address` being null'ish, but `record.address.shipping` could still be null'ish, and we want to skip calling `formatLabel(..)` in that case; that's why we still need the final `!= null` check.
The `Maybe` monad -- sometimes referred to by different names like `Option` or `Optional` in other libraries and languages -- allows us to define a behavior that delegates these sorts of `!= null` checks completely to the monad behavior, freeing up our code from that burden.
To understand `Maybe`, let's first add another monad kind besides `Just` (identity) we discussed previously: the trivial-and-unimpressive monad we'll call `Nothing` (empty). `Nothing` does even less than `Just`: it short-circuits out of any methods you invoke on it. It's like a blackhole where operations are safely skipped as no-ops. `Nothing` is the safe, monadic equivalent of empty values like `null` or `undefined`.
`Maybe` is a *Sum Type*, in that it multiple (two) monad kinds (`Just` and `Nothing`). That's not to say a `Maybe` instance is *both* simultaneously, but rather that it can *either* be one or the other.
**Note:** Most monad implementations would not expose `Just` and `Nothing` as separate monad kinds, but rather only as part of `Maybe`. **Monio** choose to present them separately as well as combined in `Maybe`, for convenience of illustration purposes.
The way the selection between `Maybe:Just` (aka `Just`) and `Maybe:Nothing` (aka `Nothing`) occurs might be a little confusing at first. You might expect the decision itself to be built into the unit constructor `Maybe(..)`. In fact, most popular monad tutorials/blog posts out there in the wild do just that, because it makes the illustration of `Maybe` much more convenient and satisfying.
That's not proper monad'ing, though. **Monio** does the more appropriate thing and externalizes the decision away from the `Maybe(..)` / `Maybe.of(..)` constructor, into a separate helper called `Maybe.from(..)`. `Maybe.from(null)` will result in a `Maybe:Nothing{}` instance, and `Maybe.from(42)` will result in a `Maybe:Just{42}` instance.
By contrast, calling `Maybe(..)` / `Maybe.of(..)` will not do any conditional selection, but only represent any non-`Maybe` value as a `Maybe:Just`.
Moreover, `Maybe.from(..)` delegates the question -- "is it empty (aka null'ish)?" -- to the static function `Nothing.isEmpty(..)`. That function by default does a `== null` null'ish check, but you could override it to re-define what value(s) you want to treat as empty/nothing for `Maybe.from(..)`'s purposes.
So we use `Maybe.from(..)` to create either the `Maybe:Just` or `Maybe:Nothing` instance. In the following snippet, the `.chain(..)` calls will thus in effect be against one or the other (with the `Maybe` itself acting merely as a thin, pass-through wrapper):
```js
const shippingLabel = (
Maybe.from(record)
.chain( record => Maybe.from(record.address) )
.chain( address => Maybe.from(address.shipping) )
.chain( shipping => Maybe.from(formatLabel(shipping)) )
);
```
Here, `shippingLabel` is an instance of the `Maybe` type. It will either represent a `Maybe:Just` holding the formatted label, or it will represent a `Maybe:Nothing` (holding no value).
But whichever one it is, that doesn't matter to the way we write our subsequent monad-aware code! If `Maybe.from(..)` produces a `Maybe:Nothing` anywhere along that chain, any subsequent `chain(..)` calls are skipped as no-ops, thus protecting our program from exceptions like property access on a null'ish value.
`Maybe` safely abstracts away our previous concerns over the conditional decision logic that protects operations from throwing exceptions.
That above code may seem a little cumbersome, so let's further clean it up with a couple of helpers:
```js
// assumed:
// function formatLabel(label) { .. }
// helpers:
const getPropSafe = prop => obj => Maybe.from(obj[prop]);
const formatLabelSafe = v => Maybe.from(formatLabel(v));
const shippingLabel = (
Maybe.from(record)
.chain( getPropSafe("address") )
.chain( getPropSafe("shipping") )
.chain( formatLabelSafe )
);
```
That's much nicer than before. And there's no `!= null` checks cluttering up our code.
Our `shippingLabel` monad is now ready to interact with other monads/monad behaviors, and will do so safely and predictably, regardless of whether it's `Maybe:Just` or `Maybe:Nothing`.
One further improvement to the code can be made using a convenience that **Monio** provides: a helper sub-method on its monads' `map(..)` / `chain(..)` / etc methods, called `.pipe(..)`, as shown here:
```js
const shippingLabel = (
Maybe.from(record)
.chain.pipe(
getPropSafe("address"),
getPropSafe("shipping"),
formatLabelSafe
)
);
```
As you can see, `chain.pipe(..)` allows you to compose multiple subsequent `.chain(..)` calls into a single call with each subsequent argument listed in order. You can do the same with `.map.pipe(..)`, `.ap.pipe(..)`, and `.concat.pipe(..)`, on any **Monio** monads. In addition to the added convenience, in some cases (e.g., `.map.pipe(..)`), it's also slightly more efficient/performant!
By the way, `Maybe` is also [Foldable](#foldable), so to "exit" from it (as we saw earlier with `Just`), you can use the `fold(..)` function; but since `Maybe` is a *Sum Type*, `fold(..)` here expects two functions, the first invoked for `Maybe:Nothing` and the second invoked for `Maybe:Just`. Again, more on [using Foldable and other adjacent behaviors later](#-and-friends).
You're hopefully starting to see a *little bit* more benefit to representing our values/expressions with monads rather than *just* using bare values.
### Reader Monad
One of the key principles of FP is avoiding side-effects; in JS, the global object (`window`, `globalThis`) is one of the most tempting sources of side-effects. If we can isolate our program and "parameterize" the global-context -- aka, the "environment" -- that it runs in, we can go a long way toward avoiding unwanted side-effects. It also makes our programs easier to test.
But nobody wants to pass around `window` as an argument to every single operation.
Let's first re-visit an earlier monad, `Just`. It might seem like we could just put our *environment* (e.g., `window`, etc) into a `Just` instance, and automatically we'd have that value passed in when we `map(..)` / `chain(..)` off our monad instance(s).
```js
var baseCtx = Just(window);
// later:
baseCtx.map(global => {
// ..
return global;
});
```
Of course, if we `return` a different (or modified!) value from a `map(..)` / `chain(..)` step, we've *propagated* our modified environment forward to the next operation.
#### From Eager To Lazy
But one key limitation of this approach is that we have to hard-code this *environment* value **at the initial step** of our operations. That makes it much more awkward to re-use that sequence of operations (i.e., our program) in different contexts -- for example, running in production versus running unit tests against a "mock" global object context.
A great solution here is *laziness*. This means we can define a monadic chain of operations, but none of them are eagerly evaluated at definition time; evaluation is deferred by design. We can then later trigger the evaluation of these operations (e.g., calling a method like `run(..)` or `evaluate(..)`), and passing in the *environment* value to be carried through all the operations.
For example, consider an imagined `LazyJust` (not actually part of **Monio**):
```js
var baseCtx = LazyJust();
baseCtx.map(global => {
// ..
});
// later:
baseCtx.evaluate(window);
// in tests:
baseCtx.evaluate(mockWindow);
```
Notice that we can actually evaluate our `baseCtx` chain independently as many times as necessary, and each invocation we specify what *environment* to use for that invocation. This is a simple but big improvement!
#### Beyond Value Containers
So far, we've seen monads that represent (contain, hold!) concrete values like `42` or a shipping address object. While the lazily-provided *environment* value itself is obviously a concrete value, this imagined `LazyJust` conceptually is not a "container" for such a value. It's more of a template, or an abstract-placeholder container, whose value is not concretely filled in until each `evaluate(..)` invocation.
Fortunately, monads are far more than just "value wrappers". Monads can also be thought of as "behavior wrappers", representing operations (functions) for various purposes.
**Note:** If those operations are impure (have side effects), I'd strongly suggest a more advanced lazy monad, [IO](#i-know-io) (more on that later).
While **Monio** does not define an explicit `Reader` monad, we can imagine one with this minimal proof-of-concept code:
```js
// not officially included in Monio, just a POC sketch for illustration purposes:
function Reader(evaluate = env => env) {
return { map, chain, evaluate };
function map(mapFn) {
return chain(env => Reader(() => mapFn(env)));
}
function chain(chainFn) {
return Reader(env => {
var env2 = evaluate(env);
return chainFn(env2).evaluate(env2);
});
}
}
```
As illustrated here, `Reader` takes an *optional* function (called `evaluate(..)` here). It should be a pure function that performs any necessary processing (if any) to transform -- remember, always avoid mutation if possible! -- the *environment* value as passed in. By default, the evaluation function is the *identity* function and passes the value through untouched.
Reader is a useful monad for multiple tasks. As mentioned above, it can carry (implicitly pass along) a global-like object (e.g., `window`) as its *environment*, even if no transformation to the *environment* is necessary.
But Reader can actually carry any value (object, etc) you want. And if there are multiple steps in the Reader chain, each step can choose to transform the value as it cascades down the chain, as shown here:
```js
// not officially included in Monio, see above
var r = Reader();
r.evaluate({ x: 1 });
// { x: 1 }
r
.map(env => ({ ...env, y: 2 }))
.chain(env => Reader(env => ({ ...env, z: 3 })))
.evaluate({ x: 1 });
// { x: 1, y: 2, z: 3 }
```
**Note:** The `chain(..)` call above includes (for illustration purposes) two references to `env`, one passed directly to the chain callback, and one that's implicitly wired into the returned `Reader` instance. They're both the same value, and if you use the same `env` name as above for both, the inner `env` lexically shadows (hides from access) the outer `env`. To make the code clearer, pick one or the other to use in such a situation.
So keep in mind that Reader is not really a *container* for an environment value. It's a lazy way to access and carry along a value (*environment*) implicitly. We'll revisit this non-container-ness later, in the `IO` section.
### `State`fully Monadic
**Monio Reference: [`State`](MONIO.md#state-monad)**
It's no surprise to assert that the most important task in our programs it to manage state. And there's no shortage of patterns for state management. Surely, monads offer some assistance!?
At the outset, you might consider *just* holding a state object in a `Just`, and modifying it as necessary. Of course, mutation of an object not only violates FP purity rules, but also violates the assumed foundations of all monadic operations. You can avoid that mutation no-no with a little bit of extra discipline:
```js
var state = Just({ counter: 4 });
// later:
state = state.map(st => ({ ...st, counter: st.counter + 3 }));
state; // Just{{ counter: 7 }}
```
We keep our state object in a monad, and whenever we want to change it, we call `map(..)` or `chain(..)`, and copy the current state's contents to a new state object, including any updates to state properties as necessary. But is that actually doing anything useful (or monadic!?) for us? Honestly, no. There's nothing there you can't get from having `state` hold an object directly without any monad wrapper.
The [Reader monad](#reader-monad) we illustrated previously is a bit more ergonomic for simple object-like state management than `Just`, as shown here:
```js
// using the hypothetical (non-Monio) Reader utility:
var state = Reader();
// later:
state = state.map(st => ({ ...st, counter: st.counter + 3 }));
state.evaluate({ counter: 4 });
// { counter: 7 }
```
Why then is there a dedicated State monad type, if Reader seems perfectly simple and straightforward for the task?
State is lazy like Reader, and the implicitly carried value is usually referred to as *state* instead of *environment*. But unlike Reader, State also produces an optional *output value* along with each usage/transformation of its *state*.
State is like `Just` + `Reader`. In other words, State is a more feature-ful Reader.
In practical terms, this means that a `State` instance will optionally hold a value (of any type) that it passes directly to `map(..)` and `chain(..)` callbacks. And it will also carry in parallel a *state* (again, of any type, but often an object), which can be transformed (using `chain(..)` only).
And when you `evaluate(..)` the lazy `State` operation(s), the result is a *pairing* (object-tuple) of both the *output* `value` and the carried `state`.
```js
var state = State.of();
// later:
state = state
.map(v => v ?? 21)
.chain(v => (
State(st => ({
value: v * 2,
state: { ...st, counter: st.counter + 3 }
}))
));
state.evaluate({ counter: 4 });
// {
// value: 42,
// state: {
// counter: 7
// }
// }
```
**Note:** The above snippet initializes the instance with `undefined` value, but then effectively injects the value `21` via the `map(..)` step. You can more directly construct a `State` instance with a value using `State(st => ({ value: 21 , state: st }))`, or the convenience-preferred `State.of(21)`.
#### Why `State` Though?
`State` is more powerful than Reader. But you may be wondering why we'd use it, especially if we don't immediately conceive of a reason for the separate tracking of the *output value* alongside tracking *state*?
Let's look at one more example where the *pairing* in `State`'s tracking can be helpful. We're collecting a list of user `records`, each with its own uniquely assigned (automatically incrementing integer) ID. The state we need to track as we compile the user records is an incrementing counter for each *type* of user ("author", "reader").
We *could* keep `records` in the same *state* object as the counters. But conceptually, the *state* is secondary (i.e., supporting) to the main purpose of the operation, the list of records. Beyond conceptual separation, we also avoid any accidental naming collisions in the *state* object.
Moreover, at the end of the `evaluate(..)` call, we might be ready to discard that temporary counters state and only keep/use the `value` (list of records). It thus hopefully makes *sense* to keep them separate.
Consider:
```js
const getID = type => State(st => {
var newState = {
...st,
[type]: (st[type] ?? 0) + 1
};
var id = `${type}-${newState[type]}`;
return {
value: id,
state: newState
};
});
const newRecord = (type,name) => records => (
getID(type)
.map(id => ([
...records,
{ id, name }
]))
);
State.of(/*records=*/[])
.chain(newRecord("author","Kyle"))
.chain(newRecord("reader","Frank"))
.chain(newRecord("author","Sarah"))
.chain(newRecord("reader","Bob"))
.chain(newRecord("reader","Beth"))
.evaluate(/*idCounters=*/{});
// {
// value: [
// { id: "author-1", name: "Kyle" },
// { id: "reader-1", name: "Frank" },
// { id: "author-2", name: "Sarah" },
// { id: "reader-2", name: "Bob" },
// { id: "reader-3", name: "Beth" }
// ],
// state: { author: 2, reader: 3 }
// }
```
Each `chain(..)` call passes in the current compounding value of the `records` list. The `newRecord(..)` function invokes `getID(..)` to produce a new `State` instance, which will ultimately be evaluated in the *state* context ("idCounters"-labeled object). `map(..)` turns the returned string `id` value into the now-appended `records` list, which is then passed along to the next `chain(..)` step.
The evaluator function passed to `State(..)` takes care of amending the state (into `newState`) with the updated counter, and also assembling the new `id` value for that `getID(..)` invocation.
As an exercise for the *reader*, consider trying the above code with only the `Reader` or `Just` monads, and then compare the approach to what's presented here. Hopefully that helps solidify the *value* of `State`.
### I Know, IO
**Monio Reference: [`IO` (and variants)](MONIO.md#io-monad-one-monad-to-rule-them-all)**
Similar to the [Reader monad type](#reader-monad) or [State monad type](#statefully-monadic) representing an operation to compute a new state (and optional output value), IO monads implicitly carry a value across (lazily-evaluated) operations, but specifically ones that either rely on, or cause, side effects.
The heart of **Monio** is its `IO` monad implementation, an uber-powerful *Product Type* (as opposed to *Sum Type*) that composes a variety of useful behaviors, similar in spirit and form to [Scala's ZIO](https://zio.dev/). I claim that **Monio**'s `IO` is the "most powerful IO implementation in JS (and possibly any language)". But I know that's a dubious-sounding claim.
We're only going to focus on a small part of what `IO` can do, just so we don't get too overwhelmed.
Because IO is lazy (like State and Reader), you represent its side effect(s) with a function -- though the function may be pure and simply return a value. When you `run(..)` an IO instance, the side effect(s), if any, are applied to your program (specifically, the environment passed in), and the result, if any, is returned.
For example:
```js
const greeting = IO(() => console.log("Hello, friend!"));
// later (nothing has happened yet!)
greeting.run();
// Hello, friend!
```
**Note:** Yes, even something so mundane as printing a message to the developer console, *is* I/O, a program side effect! It *belongs* in an `IO`, if you're following monadic design.
An IO, once evaluated, may also produce a value:
```js
const customerName = IO(() => (
document.getElementById("customer-name-input").value
));
customerName.run(); // "Kyle"
```
The `run(..)` method is analogous to the `evaluate(..)` of `State`, or the `fold(..)` method we saw on `Just(..)` and `Maybe(..)`. It's how you "exit" the `IO` monad, applying its behavior (side-effects) to the surrounding program.
But you don't get much out of IO if you put a single operation into it and then immediately execute/apply that operation. The key benefit of IO is to represent all of your program's side-effect operations as IO instances, chained together, and only evaluate the composition of them as the final step of program execution.
Here's a more sophisticated example that chains `IO` instances together:
```js
// helpers:
const getProp = prop => obj => obj[prop];
const assignProp = prop => val => obj => obj[prop] = val;
const getElement = id => IO(() => document.getElementById(id));
const getInputValue = id => getElement(id).map( getProp("value") );
const renderTextValue = id => val => (
getElement(id).map( assignProp("innerText")(val) )
);
const renderCustomerNameIO = (
getInputValue("customer-name-input")
.chain( renderTextValue("customer-name-display") )
);
// later:
renderCustomerNameIO.run();
```
As you can see, here we're composing side-effect operations together as predictably as we composed numbers and objects earlier. Monads truly are a transformative, revolutionary way of thinking about our programs.
#### Non-Value-Container?
Recall earlier in the Reader discussion, we pointed out that `Reader` monad instances are not really value containers. The same is true of `State` and now `IO`. Yes, we instantiate `IO` *with* a function, but... this function is not *the value* that's "held" by the instance in the most meaningful sense..
Consider `IO(() => 4)`, or the convenience equivalent `IO.of(4)`. Do either of those ways of expressing an `IO` instance actually *hold* the value `4`? No. They both *reference* a function that will do something, in this case return the value `4`.
**Warning:** Take note of a nuance/gotcha: in the `IO.of(..)` case, whatever expression (the `..` here) provided is evaluated right away, whereas when you do `IO(() => ..)`, you've manually wrapped the `..` expression, whatever it is, into a function, so it won't be evaluated until that function is called (at the time the IO is evaluated). As such, `IO.of(..)` should only be used when you have a fixed, non-side-effecting value expression. Always use the `IO(() => ..)` form when the `..` expression actually includes a side effect, to *defer* that side effect until it should be performed.
So `IO(() => 4)` (or `IO.of(4)`) doesn't *hold* the value `4`, but it also doesn't *hold* the function `() => 4` either, because there's no way to *get* that function value itself back out. There's only a way to cause that function to be evaluated, and then get *its* return value (if any): the `run()` method.
Contrast that with `Just(() => 4)`, which *is indeed* concretely holding the `() => 4` function value, and that function value can be used itself, directly.
So do IO (and Reader and State) instances *hold* anything? Not really, not concretely anyway. These types of monads are not containers in a direct sense.
They abstractly "hold" the *capability* to perform some action, which may or may not produce some tangible value (as return from `evaluate()` / `run()`).
For Reader, the *capability* is to carry (implicitly pass along) an environment. For State, the *capability* is to carry a state, and compute a new state from it, optionally with another output value. And for IO, the *capability* is to perform (and read) side effects on a context/environment.
These monad types are capability representations, not value containers.
#### But Why IO?
Why do we go to the trouble of putting all our side-effect operations into `IO` instances?
The most boiled down answer: we get a predictable interaction (and guarantees!) between the side-effect (`IO`) that comes from the `getInputValue(..)` call and the side-effect (`IO`) that comes from the `renderTextValue(..)` call.
When the side-effects are straightforward and synchronous like pulling a DOM element reference out of the DOM, or injecting its contents, it doesn't seem like the predictability/guarantees is benefitting us very much.
So really the question I want to address here is: **why Monio's `IO` in particular?**
I strongly believe the most complex side-effects in our programs come from asynchronus operations, like performing an Ajax `fetch(..)` request, running a timer, listening for an event, performing an animation, etc. An `IO` implementation like the one **Monio** provides, which can represent and model any form of asynchrony (and thus asynchronous side-effects) in our program, and thus extend our predictability and guarantees over *time*, is truly a game-changer.
**Monio**'s `IO` automatically transforms/consumes JS promises and lifts the evaluation of an `IO` chain to a promise if any asynchronous operation is encountered. And for event streams (where a single promise doesn't adequately represent the asynchrony), `IOx` is like `IO` plus Observables (e.g., `RxJS`, etc).
And if that's not enough to intrigue you, there's another challenge that programs face (whether you realize it or not) that **Monio**'s `IO` addresses: how do you isolate a set of operations from the environment (like DOM, etc) around it, so that you can provide an environment/context for the code to run against? This is critical for preventing unintended side-effects in the program, but it's also the most effective way to create **TESTABLE** side-effect code.
Like [State](#statefully-monadic), **Monio**'s `IO` also holds the [Reader monad type's](#reader-monad) behavior. This means that an `IO` (no matter how long/involved the chain is) carries with it a provided "environment", passed as an argument to `run(..)`.
You could define your entire program to boil down to a single `IO` instance, and if you call `run(window)`, you're running your program in the context of the browser's DOM. But in your test suite, you could call `run(fakeDOMglobal)` on the same `IO`, and now all of the code and side-effects are automatically applied against that alternate environment.
It's effectively passing the entire "global" (aka, universe/scope-of-concern) into your program, whatever appropriate value that is, instead of the program automatically assuming which "global" it should apply against.
But ultimately, the *real power* of **Monio**'s `IO` is not even encompassed by what we've thus far discussed. The *pièce de résistance* is that `IO` provides a bridge back to your familiar and comfortable more-imperative style coding.
Do you like to use `if` and `try..catch` and `for..of` loops? You may have noticed that FP and monads seem to throw all that stuff out the window, in favor of long chains of curried and composed function calls. What if you could get all the power of `IO` (and other monads!) but opt-in to the more typically-imperative style of code where helpful?
Various FP languages like Haskell provide what's called a "do syntax" for monads like IO to accomplish such a feat.
**Monio**'s do-syntax for JS comes through `IO.do(..)`, which takes a JS generator whose code looks like the `async..await` style that most JS devs are so familiar with. When you `yield` a value, if its monadic, it's automatically chained and unwrapped (just as if you had an `IO` to `chain(..)` from). And if the result is asynchronous (a promise), the code inside the generator automatically pauses to "await" the completion.
Here's the previous IO snippet, expressed with `IO.do(..)`:
```js
// helpers:
const getProp = prop => obj => obj[prop];
const assignProp = prop => val => obj => obj[prop] = val;
const getElement = id => IO(() => document.getElementById(id));
const getInputValue = id => getElement(id).map( getProp("value") );
const renderTextValue = id => val => (
getElement(id).map( assignProp("innerText")(val) )
);
// do-style instead of chaining-style:
const renderCustomerNameIO = IO.do(function *renderCustomerNameIO(env){
var val = yield getInputValue("customer-name-input");
return renderTextValue("customer-name-display")(val);
});
// later:
renderCustomerNameIO.run();
```
The `IO.do(..)` replaces the IO `.chain(..)` style with a more familiar imperative style, but with all the monadic guarantees underneath. It's the best of both worlds!
Taken together with all its facets, that's why I claim **Monio**'s `IO` (and `IOx` superset) is the "one monad to rule them all".
### Monad *Sum*mary
We've now explored the ideas behind several monads, from Identity (e.g., `Just`) through Maybe, Reader, State, and finally IO.
We skipped over Either -- another *Sum Type* like Maybe, but which holds values on both sides (typically called "Left" and "Right"). Either is typically used to represent synchronous `try..catch` style exception handling. **Monio** provides `Either` (as `Left` and `Right`), as well as a monad transformer called `AsyncEither`, which extends `Either` to operate asynchronously (over promises), the same way `IO` transforms/handles them. `AsyncEither` is essentially **Monio**'s representation of a Promise/Future monad type.
Remember that *Monad* is a pattern that unifies all these specific monad types. What's common among them?
1. They all provide a "unit constructor" to produce values (i.e., construct instances) of that monad type.
2. They all define a "chain" operation (exposed as a `chain(..)` method on the instance).
These two characteristics together satisfy the **3 Monad Laws**.
Our main take-away is that if we obey the monad laws in an implementation of some data structure, as the **Monio**-provided monad types (`Just`, `IO`, etc) do, we can rely on predictable behaviors and interactions among them. That allows us to assemble our programs much more reliably and soundly, than the often quite imperative equivalents that most programs are built with.
## ... And Friends
That's a lot we've covered. Time to take a deep breath. Seriously, maybe go for a walk to let some of this settle in. *Maybe* re-read it, a few times.
Now, compared to the expanse of Category Theory, *Monad* is a fairly narrow concept itself. There are a variety of adjacent (and somewhat related) concepts -- more specifically, "Algebraic Data Types" (ADTs) -- that are helpful companions to monads. These "friends" include:
* Foldable
* Concatable (aka, Semigroup)
* Applicative
There are many, many other topics out there, but these are the main three "friends" that **Monio** focuses on (and mixes with its monads).
To be clear, these three are *not* monad behaviors. I call them "friends of monads" because I find monads mixed with these other behaviors to be more useful/practical in my JS code than monads (or any of these other types) standing alone; it's the combination of these type behaviors that I think makes monads attractive and powerful solutions for our programs.
I know many in the FP space prefer to think of each type completely independently. That's OK if it works well for them. But I find the combinations below much more compelling.
### Foldable
The `fold(..)` method mixed into (most of) **Monio**'s monads is implementing behavior from the "Foldable" type. Notably, `IO` and its variations are not directly Foldable, but that's because the nature of `IO` is already doing a `fold(..)` of sorts when you call `run(..)`.
We already saw `fold(..)` referenced earlier a few times. That's merely the name **Monio** provides, but just like `chain(..)` vs `flatMap(..)` vs `bind(..)`, the name itself doesn't matter, only the expected behavior.
We didn't talk about List type monads (because **Monio** doesn't provide such), but of course those can exist. Foldable in the context of such a List monad would apply the provided function across all the values in the list, progressively accumulating a single result (of any type) by folding each value into the accumulator. JS arrays have a `reduce(..)` method which is basically List's foldable.
By contrast, Foldable in the context of a single-value monad (like `Just`) executes a provided function with its single associated/underlying value. It can be thought of as a special case of the generalized List foldable, since it doesn't need to "accumulate" its result across multiple invocations.
Similarly, *Sum Types* like `Maybe` and `Either` are also Foldable in **Monio**; this is a further specialization in that `fold(..)` here takes two functions, but will execute only one of them. If the associated value is a `Maybe:Nothing`, the first function is applied, otherwise (when the associated value is a `Maybe:Just`), the second function is applied. The same goes for `Either:Left` invoking the first function and `Either:Right` invoking the second function.
But how might we use Foldable practically?
As I implied earlier a few times in this guide, one such transformation is the sort-of "unwrapping" of the underlying/associated value from its monad, by passing the identity function (e.g., `v => v`) to `fold(..)`.
```js
Just(42).fold(identity); // 42
```
Now, if you're looking closely, for a single value monad kind like `Just`, `fold(..)` and `chain(..)` seem to have the same behavior (and even implemented virtually identically). You may then wonder why we should provide the seemingly duplicative `fold(..)` on `Just` instead of *just* providing `chain(..)`?
As [explained earlier](#monadic-chain), the implied *type* intent is that a function provided to `chain(..)` always returns the same kind of monad as the one the `chain(..)` method was invoked on. In other words, the (Haskell'ish) type signature is essentially, `chain: m a -> (a -> m b) -> m b`. The monad of type/kind `m` may under the covers be associated with (hold) a different type value (`a` vs `b`) from before to after the `chain(..)` call, but it's still supposed to be an `m` kind monad.
So calling `Just(42).chain(identity)` violates this implied type signature -- though **Monio** doesn't enforce it and the operation would work just fine. `fold(..)` on the other hand does not have that sort of implied type signature, as it's intended for you to "fold down" to any arbitrary type, not just another monad instance. `fold(..)` then is a more flexible route that would allow us to "extract" the associated/underlying value.
Moreover, Foldable's `fold(..)` on the *Sum Types* `Maybe` and `Either` has a very different signature from their `chain(..)` method, so they're not at all duplicative of each other.
Rather than using Foldable to extract the value itself, we'll more often prefer to use `fold(..)` to define a *natural transformation* from one kind of monad to another. To illustrate, let's revisit this example from earlier:
```js
// assumed:
// function formatLabel(label) { .. }
// helpers:
const getPropSafe = prop => obj => Maybe.from(obj[prop]);
const formatLabelSafe = v => Maybe.from(formatLabel(v));
const shippingLabel = (
Maybe.from(record)
.chain.pipe(
getPropSafe("address"),
getPropSafe("shipping"),
formatLabelSafe
)
);
```
If we want to then render the shipping label, but only if it's actually valid/defined, and otherwise print a default notice, we can arrange our program like this:
```js
// assumed:
// function formatLabel(label) { .. }
// helpers:
const identity = v => v;
const getPropSafe = prop => obj => Maybe.from(obj[prop]);
const assignProp = prop => val => obj => (
Maybe.from(obj).map(o => o[prop] = val)
);
const getElement = id => IO(() => document.getElementById(id));
const renderTextValue = id => val => (
getElement(id).chain(el => (
Maybe.from(el).fold(
IO.of,
assignProp("innerText")(val)
)
))
);
const formatLabelSafe = v => Maybe.from(formatLabel(v));
// ----
const renderShippingLabel = v => (
v.fold(
() => IO.of("--no address--"),
identity
)
.chain( renderTextValue("customer-shipping-label") )
);
const renderIO = renderShippingLabel(
Maybe.from(record)
.chain.pipe(
getPropSafe("address"),
getPropSafe("shipping"),
formatLabelSafe
)
);
renderIO.run();
```
Take your time reading and analyzing that code. It's illustrating how our monad types interact in useful ways. I promise that even if at first this code seems head-spinning -- it did for me! -- eventually you will get to understanding and even preferring code like this!
A key aspect of the snippet is `Maybe`'s `fold(..)` call in the `renderShippingLabel(..)` function, which folds down to either a fallback `IO` value if the shipping address was missing, or the computed `IO` holding the valid shipping address, and then `chain(..)`s off whichever `IO` was folded to. There's a similar thing happening in `renderTextValue(..)`. Both `fold(..)` calls are expressing a natural transformation from the `Maybe` monad to the `IO` monad.
Again, Foldable is distinct from monads. But I think this discussion illustrates how useful it is when paired with a monad. That's why it's an *honored friend*.
### Concatable (Semigroup)
Concatable, formally referred to as Semigroup, is another interesting friend of monads. You won't necessarily see it used explicitly all that often, but it can be useful, especially when using `foldMap(..)` (which is an abstraction over `reduce(..)`).
**Monio** choose to implement Concatable as the `concat(..)` method on its monads. That name is not required by the type, of course, it's just how **Monio** does it.
The basic idea here is that a value type is "concatable" if two or more values of it can be concatenated together. For example, primitive, non-monad value types like strings and arrays are concatable, and indeed they even expose the same `concat(..)` method name:
```js
"Hello".concat(", friend!"); // "Hello, friend!"
[ 1, 2, 3 ].concat( [ 4, 5 ] ); // [1,2,3,4,5]
```
Since all of **Monio**'s monads are Concatable, they all have the `concat(..)` method. So if any such monad instance is associated with a value that also has a conforming `concat(..)` method on it -- for example, another monad, or a non-monad value like a string or array -- then a call to the monad's `concat(..)` method will delegate to calling `concat(..)` on the associated/underlying value. This delegation to the underlying `concat(..)` is recursive all the way down.
For example:
```js
Just("Hello").concat(Just(", friend!")); // Just("Hello, friend!")
Just([1,2,3]).concat(Just([4,5])); // Just([1,2,3,4,5])
Just(Just([1,2,3])).concat(Just(Just([4,5]))); // Just(Just([1,2,3,4,5]))
// `fold(..)` and `foldMap(..)` provided in
// Monio's util module
fold(Just("Hello"),Just(", friend!")); // Just("Hello, friend!")
foldMap(
v => v.toUpperCase(),
[
Just("Hello"),
Just(", friend!")
]
); // Just("HELLO, FRIEND!")
```
As with `chain(..)`, **Monio**'s `concat(..)` is *supposed* to be used between two same-kind monads. But there's no explicit type enforcement to prevent crossing kinds (e.g. between `Maybe` and `Either`).
**NOTE:** Despite the name overlap, the standalone `fold(..)` and `foldMap(..)` utilities provided by the `MonioUtil` module are *not* the same as the [Foldable type](#foldable)'s `fold(..)` method that appears on **Monio** monad instances.
#### Monoid
Additionally, the term Monoid means a Concatable/Semigroup plus an "empty" (identity) value for the concatenation. For example, string concatenation is a monoid with the empty `""` string. Array concatenation is a monoid with the empty `[]` array. Even numeric addition is a monoid with the `0` "empty" number.
An example of extending this notion of monoid to something that wouldn't seem at first as "concatable" is with multiple booleans combined in a `&&` or `||` logical expression. For the logical-AND operation, the "empty" value is `true`, and for the logical-OR operation, the "empty" value is `false`. The "concatenation" of these values is the computed logical result (`true` or `false`).
**Monio** provides `AllIO` and `AnyIO` as `IO` variants that are monoids -- again, both an "empty" value and a `concat(..)` method. In particular, the `concat(..)` method on these two `IO` variants is designed to compute the logical-AND / logical-OR (respectively) between two boolean-resulting IOs. That makes `AllIO` and `AnyIO` easy to use with the `fold(..)` and `foldMap(..)` utilities mentioned earlier.
**Monio Reference: [`AllIO`, `AnyIO`](MONIO.md#other-helpful-io-variants)**
Here's an example of concatenating these monoids via `fold(..)` / `foldMap(..)`:
```js
const trueIO = IO.of(true);
const falseIO = IO.of(false);
fold( AllIO.fromIO(trueIO), AllIO.fromIO(falseIO) ).run(); // false
fold( AnyIO.fromIO(falseIO), AllIO.fromIO(trueIO) ).run(); // true
const IObools = [
trueIO,
trueIO,
trueIO,
falseIO,
trueIO
];
foldMap( AllIO.fromIO, IObools ).run(); // false
foldMap( AnyIO.fromIO, IObools ).run(); // true
```
As an added convenience, **Monio**'s' `IOHelpers` module also provides `iAnd(..)` and `iOr(..)`, which automatically applies this logical-And / logical-Or `foldMap(..)` logic to two or more provided `IO` instances:
```js
const trueIO = IO.of(true);
const falseIO = IO.of(false);
iAnd( trueIO, trueIO, falseIO, trueIO ).run(); // false
iOr( trueIO, trueIO, falseIO, trueIO ).run(); // true
```
I'm illustrating creating `IO` instances with direct `true` and `false` values, but that's not really how you'd actually use these mechanisms. Since they're all `IO` instances, the boolean results (`true` or `false`) can be computed lazily (and asynchronously!) in their respective `IO`s, even as a result of complex side-effects.
For example, you could define a list of several `IO` instances representing DOM element states, like this:
```js
// helpers:
const getElement = id => IO(() => document.getElementById(id));
const getCheckboxState = id => getElement(id).map(el => !!el.checked);
const options = [
getCheckboxState("option-1"),
getCheckboxState("option-2"),
getCheckboxState("option-3")
];
const allOptionsChecked = iAnd( ...options ).run(); // true / false
const someOptionsChecked = iOr( ...options ).run(); // true / false
```
Bonus exercise: contemplate how you'd compute `noOptionsChecked` -- `true` when none of the checkboxes are checked.
### Applicative
Applicative is a bit more unusual (and less common, in my experience) than Semigroup. But occasionally it's helpful. **Monio** chooses to implement this behavior on most of its monads with the `ap(..)` method.
Applicative is a pattern for holding a function in a monad, then "applying" the value from another monad as an input to the function, returning the result back to another monad. If the function requires multiple inputs, this "application" can be performed multiple times, providing one input at a time.
But I think the best way to explain Applicative is to just show concrete code:
```js
const add = x => y => x + y;
const addThree = Just(add(3)); // Just(y => 3 + y)
const four = Just(4); // Just(4)
addThree.ap(four); // Just(7)
```
If a monad is holding a function, such as the curried/partially-applied `add(..)` shown here, you can "apply" it to the value held in another monad. Note: we call `ap(..)` on the source, function-holding monad (`addThree`), not on the target value-holding monad (`four`).
These two expressions are roughly equivalent:
```js
addThree.ap(four);
four.map( addThree.fold(fn => fn) );
```
Recall from [Foldable above](#foldable) that passing the identity function into a **Monio** monad's `fold(..)`, essentially extracts the value from the monad. As shown, `ap(..)` is sort of "extracting" a mapping function held in second monad and running it (via `map(..)`) against the value held in the first monad.
Another way of expressing the above in a single expression:
```js
const add = x => y => x + y;
Just(add) // Just(x => y => x + y)
.ap( Just(3) ) // Just(y => 3 + y)
.ap( Just(4) ); // Just(7)
```
We put `add(..)` by itself into a `Just`. The first `ap(..)` call "extracts" that function, passes the `3` into it, and makes another `Just` with the returned `y => 3 + x` function in it. The second `ap(..)` call then does the same as the previous snippet, extracting that `y => 3 + x` function and passing `4` into it. The final result of `4 => 3 + 4` is `7`, and that's put back into a `Just`.
As with `chain(..)` and `concat(..)`, **Monio**'s `ap(..)` *should* be passed the same kind of monad as the method is invoked on. But there's no explicit type enforcement to prevent crossing kinds (e.g. between `Maybe` and `Either`).
Most of **Monio**'s monads (except for `AllIO` / `AnyIO`) are Applicatives. Again, you may not use such behavior very frequently, but hopefully you may now be able to recognize the need when it arises.
## *Wrap*ping Up
We've now scratched the surface of monads (and several *friends*). That's by no means a complete exploration of the topic, but I hope you're starting to feel they're a little less mysterious or intimidating.
A monad is a narrow set of behavior (required by "laws") you associate with a value or operation. Category Theory yields other adjacent/related behaviors, such as Foldable and Concatable, that can augment the capabilities of this representation.
This set of behavior improves coordination/interoperation between other monad-and-friends-compliant values, such that results are more predictable. The behaviors also offer many opportunities to abstract (shift into the behavioral-definitions) certain logic that usually clutters up our imperative code, such as null'ish checks.
Monads certainly don't fix all the problems we may encounter in our code, but I think there's plenty of intriguing power to unlock by exploring them further. I hope this guide inspires you to keep digging, and perhaps in your explorations, you'll find the [Monio](https://github.com/getify/monio) library helpful.