(Video available at http://fsharpforfunandprofit.com/monadster/)
You've got a pile of assorted functions lying around. Each one is useful and reliable, but they just don't fit together properly. How can you assemble them into a complete system that can stand on its own two feet and terrorize the local villagers?
In this session, I'll show how functional programming can transform all sorts of existing code into shapes that are plug-compatible and which can be bolted together effortlessly.
SAFETY NOTE: The techniques demonstrated are perfectly harmless and can even be used at your workplace -- no lightning bolts required.
13. The spread of the monad
• 1990 ACM Conference on LISP and Functional Programming
First monad in captivity
14. The terrible events at the 1990 ACM Conference on
LISP and Functional Programming
15. The spread of the monad
• 1990 ACM Conference on LISP and Functional Programming
• 1991 Eugenio Moggi, "Notions of computation and monads"
• 1992 PhilipWadler, "Monads for Functional Programming"
• 1999 Noel Winstanley, "What the hell are Monads?"
• 2004 Greg Buchholz, "Monads in Perl"
• 2005 Eric Kow, "Of monads and space suits"
• 2006 Eric Kow, Monads as nuclear waste containers
• 2009 James Iry, "A monad is just a monoid in the category of endofunctors,
what's the problem?"
• It’s everywhere now
16. The spread of the monad
• 1990 ACM Conference on LISP and Functional Programming
• 1991 Eugenio Moggi, "Notions of computation and monads"
• 1992 PhilipWadler, "Monads for Functional Programming"
• 1999 Noel Winstanley, "What the hell are Monads?"
• 2004 Greg Buchholz, "Monads in Perl"
• 2005 Eric Kow, "Of monads and space suits"
• 2006 Eric Kow, Monads as nuclear waste containers
• 2009 James Iry, "A monad is just a monoid in the category of endofunctors,
what's the problem?"
• It’s everywhere now
No wonder people think
monads are dangerous
17. The secret history of the monad
• 1816 Dr Frankenfunctor creates the Monadster
• 1990 ACM Conference on LISP and Functional Programming
• 1991 Eugenio Moggi, "Notions of computation and monads"
• 1992 PhilipWadler, "Monads for Functional Programming"
• 1999 Noel Winstanley, "What the hell are Monads?"
• 2004 Greg Buchholz, "Monads in Perl"
• 2005 Eric Kow, "Of monads and space suits"
• 2006 Eric Kow, Monads as nuclear waste containers
• 2009 James Iry, "A monad is just a monoid in the category of endofunctors,
what's the problem?"
• And 100's more
The topic of this talk
23. But how was it done?
I have devoted many years of
research into this matter...
24. At last, I can reveal the secret
techniques of Dr Frankenfunctor!
Warning: These are powerful techniques and can be
used for good or evil...
I know of a young, innocent developer
who was traumatized for life.
25. Dr Frankenfunctor's toolbox
1. Modelling with pure functions
2. Wrapping a function in a type
3. Transforming parts into other parts
4. Combining two parts into one
5. Combining live and dead parts
6. Chaining “part-creating” functions together
7. A general way of combining any number of parts
I don’t expect you to remember all this!
Goal is just to demystify and give an overview
33. Live part B
Vital force
Become alive B!
Remaining vital force
Dead part B
Live part A
Vital force
Become alive A!
Remaining vital force
Dead part A
How to connect the force
between two steps?
34. Live part B
Vital force
Become alive B!
Remaining vital force
Dead part B
Live part A
Vital force
Become alive A!
Remaining vital force
Dead part A
How to combine
the two outputs?
39. Input A
Uncurried
Function
Input B
Output C
Curried
Function
Input A
Intermediate
Function
Output CInput B
What is currying?
after currying
Currying means that *every* function has one input
40. // naming a lambda
Func<int,int> add1 = (y => 1 + y)
// using it
var three = add1(2)
Currying examples
41. // naming a lambda
let add1 = (fun y -> 1 + y)
// using it
let three = add1 2
Currying examples
42. // returning a lambda with baked in "x"
let add x = (fun y -> x + y)
// creating an intermediate function
let add1 = add 1 // (fun y -> 1 + y)
// using it
let three = add1 2
Currying examples
43. // "inlining" the intermediate function
let three = (add 1) 2
// returning a lambda with baked in "x"
let add x = (fun y -> x + y)
Currying examples
44. // removing the parens
let three = add 1 2
Currying examples
// returning a lambda with baked in "x"
let add x = (fun y -> x + y)
45. Live part A
Vital force
Become alive!
Remaining vital force
Dead part A
Currying "become alive!"
49. Dead part A
M<Live Part A>
Create step in
recipe
Wrapping the function
An "M-making" function
Remember --
this is *not* a live part ,
it's a "potential" live part
50. M<Live Part A> Run
Live part A
Remaining vital force
Running the function
Vital force
51. M<Live Part A> Run
Live part A
Remaining vital force
Running the function
Vital force
Become alive!
Vital force
Live part A
M<Live Part A>
53. let makeLiveThingM deadThing =
// the inner one-argument function
let becomeAlive vitalForceInput =
... do stuff
... return two outputs
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
54. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
55. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
56. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
57. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
58. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
59. let makeLiveThingM deadThing =
// get essence of dead thing
let essence = getEssenceOfDeadThing deadThing
// the inner one-argument function
let becomeAlive vitalForceInput =
// get a unit of vital force
let unitOfForce, remainingForce = getVitalForce vitalForce
// create a live thing
let liveThing = new LiveThing(essence, unitOfForce)
// return the live thing and remaining force
(liveThing, remainingVitalForce) // return a pair
// wrap the inner function in the "M" wrapper
M becomeAlive
Creating M-things
makeLiveThingM : DeadThing -> M<LiveThing>
60. // create DeadLeftLeg
let deadLeftLeg = DeadLeftLeg "Boris"
// create a M<LiveLeftLeg>
let leftLegM = makeLiveLeftLegM deadLeftLeg
// potential leg only!
// now pretend that vital force is available
let vf = {units = 10}
// make a real left leg by running leftLegM
let liveLeftLeg, remainingForce =
runM leftLegM vf
// output:
// liveLeftLeg : LiveLeftLeg =
// LiveLeftLeg ("Boris",{units = 1})
// remainingForce : VitalForce = {units = 9}
Demo – Left Leg
62. A Broken Arm
Dead Broken Arm
What we've got
Live Healed Arm
What we want
63. Healing a broken arm
Live Healed ArmLive Broken Arm HealBrokenArm
We have this function!
64. Live Healed Arm
...But we want one of these!
How can we get it?
Healing a broken arm
Dead Broken Arm
We have one of these...
65. Create
Dead Broken Arm Dead Healed Arm
Live Healed Arm
Healing a broken arm
HealBrokenArm
66. Create
Dead Broken Arm Dead Healed Arm
Live Healed Arm
No. We can only heal live arms
Healing a broken arm
HealBrokenArm
67. Dead Broken Arm
Live Healed ArmLive Broken Arm
Create
HealBrokenArm
Healing a broken arm
68. Dead Broken Arm
Live Healed ArmLive Broken Arm
Create
HealBrokenArm
No. We can't create live things
directly, only M-type things
Healing a broken arm
69. Dead Broken Arm
M<Live Healed Arm>M<Live Broken Arm>
Create
HealBrokenArm
Healing a broken arm
70. Dead Broken Arm
M<Live Healed Arm>
M<Live Broken Arm>
Create
HealBrokenArm
No. "HealBrokenArm" doesn't
work on M-type things
Healing a broken arm
71. Dead Broken Arm
M<Live Healed Arm>M<Live Broken Arm>
Create
HealBrokenArmM
We need a special "HealBrokenArmM"
that works on M-type things
Where can we get it from?
Healing a broken arm
72. Live Healed ArmLive Broken Arm HealBrokenArm
Healing a broken arm
M<Live Healed Arm>M<Live Broken Arm> HealBrokenArmM
This is what we’ve got
This is what we want
73. Live Healed ArmLive Broken Arm HealBrokenArm
Healing a broken arm
M<Live Healed Arm>M<Live Broken Arm> HealBrokenArmM
map
78. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
Transformation function
M-thing to transform
79. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
80. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
81. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
82. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
83. let map f bodyPartM =
// the inner one-argument function
let becomeAlive vitalForce =
// get the input body part by running the M-thing
let bodyPart,remainingVitalForce =
runM bodyPartM vitalForce
// transform the body part using the function
let transformedBodyPart = f bodyPart
// return the transformed part and remaining force
(transformedBodyPart, remainingVitalForce)
// wrap the inner function in the "M" wrapper
M becomeAlive
map :
('a -> 'b ) -> // The input is a normal function.
( M<'a> -> M<'b> ) // The output is a function in the
// world of M-things.
84. let deadLeftBrokenArm = DeadLeftBrokenArm "Victor"
let leftBrokenArmM = makeLiveLeftBrokenArm deadLeftBrokenArm
let leftHealedArmM =
// map the healing function to the world of M-things
let healBrokenArmM = map healBrokenArm
// use it!
healBrokenArmM leftBrokenArmM
// return type is M<LiveLeftHealedArm>
// run the M<LiveLeftHealedArm> with some vital force
let liveLeftHealedArm, remainingAfterLeftArm =
runM leftHealedArmM vf
Demo – Broken Arm
85. let deadLeftBrokenArm = DeadLeftBrokenArm "Victor"
let leftBrokenArmM = makeLiveLeftBrokenArm deadLeftBrokenArm
let leftHealedArmM =
// map the healing function to the world of M-things
let healBrokenArmM = map healBrokenArm
// use it!
healBrokenArmM leftBrokenArmM
// return type is M<LiveLeftHealedArm>
// run the M<LiveLeftHealedArm> with some vital force
let liveLeftHealedArm, remainingAfterLeftArm =
runM leftHealedArmM vf
Demo – Broken Arm
86. let deadLeftBrokenArm = DeadLeftBrokenArm "Victor"
let leftBrokenArmM = makeLiveLeftBrokenArm deadLeftBrokenArm
let leftHealedArmM =
// map the healing function to the world of M-things
let healBrokenArmM = map healBrokenArm
// use it!
healBrokenArmM leftBrokenArmM
// return type is M<LiveLeftHealedArm>
// run the M<LiveLeftHealedArm> with some vital force
let liveLeftHealedArm, remainingAfterLeftArm =
runM leftHealedArmM vf
Demo – Broken Arm
// output
// liveLeftHealedArm : LiveLeftHealedArm =
// LiveLeftHealedArm ("Victor",{units = 1})
// remainingAfterLeftArm : VitalForce =
// {units = 9}
88. The "map" pattern for elevated worlds
map
ElevatedWorld
NormalWorld
a b
ElevatedWorld
Normal World
E<a> E<b>
A function in the world of normal things
where "elevated world" is
Option, List, Async, etc
89. The "map" pattern for elevated worlds
map
ElevatedWorld
NormalWorld
a b
ElevatedWorld
Normal World
E<a> E<b>
A function in the world of E-things
where "elevated world" is
Option, List, Async, etc
90. The importance of map
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
91. The importance of map
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
92. The importance of map
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
93. let addTwo_L inputList =
let outputList = new List()
foreach element in inputList do
let newElement = addTwo element
outputList.Add(newElement)
How not to code with lists
Let’s say you have some ints wrapped in an List, and
you want to add 2 to each element:
let addTwo x = x + 2
94. let addTwo_L inputList =
let outputList = new List()
foreach element in inputList do
let newElement = addTwo element
outputList.Add(newElement)
How not to code with lists
Let’s say you have some ints wrapped in an List, and
you want to add 2 to each element:
let addTwo x = x + 2
95. let addTwo_L inputList =
let outputList = new List()
foreach element in inputList do
let newElement = addTwo element
outputList.Add(newElement)
How not to code with lists
Let’s say you have some ints wrapped in an List, and
you want to add 2 to each element:
let addTwo x = x + 2
96. How not to code with lists
addTwo
World of normal values
World of Lists
97. How to code with lists
addTwo_L
World of normal values
World of Lists
98. How to code with lists
T -> U
List<T> -> List<U>
List.map
World of normal values
World of Lists
Linq.Select
99. How to code with lists
addTwo
addTwo_L[1;2] |>
1 |> // 3
// [3;4]
World of normal values
World of Lists
100. // map works with "addTwo"
let addTwo_L = List.map addTwo // List<int> -> List<int>
addTwo_L [1;2] // List<int> = [3; 4]
[1;2] |> List.map addOne // List<int> = [3; 4]
// map works with "healBrokenArm"
let healBrokenArm_L = List.map healBrokenArm
// List<LiveLeftBrokenArm> -> List<LiveLeftHealedArm>
Same applies for any generic type:
Option, Task, etc
108. Create
ArmSurgery
No. We can't make live things
directly, only M-type things
Combining two parts
Dead Lower Arm Dead Upper Arm
Live Lower Arm Live Upper Arm Live Arm
Create
110. Create
ArmSurgeryM
Combining two parts
Dead Lower Arm Dead Upper Arm
M<LiveLowerArm> M<LiveUpperArm> M<LiveArm>
Create
We need a special "ArmSurgeryM"
that works on M-type things
112. World of things
World of M<_> things
Param1 -> Param2 -> Result
map2
A 2-param function in
the world of things
The "map2" pattern for M-things
113. A 2-param function in
the world of M<thing>s
World of things
World of M<_> things
Param1 -> Param2 -> Result
M<Param1> -> M<Param2> -> M<Result>
map2
The "map2" pattern for M-things
114. A 2-param function in
the world of E<thing>s
World of things
World of E<_> things
Param1 -> Param2 -> Result
E<Param1> -> E<Param2> -> E<Result>
map2
The "map2" pattern for elevated worlds
Applies to any generic type: Option, Task, etc
132. Chaining functions
Dead Heart M<Live Heart> Live Heart M<Beating Heart>
We have an
M-generating function
We have another
M-generating function
133. Dead Heart M<Live Heart>
Live Heart M<Beating Heart>
Chaining functions
Output type doesn't
match input type
134. Dead Heart M<Live Heart>
Live Heart M<Beating Heart>
Chaining functions
135. Dead Heart M<Live Heart>
M<Live Heart> M<Beating Heart>
Chaining functions
If we could change this type to
M<Live Heart>, it would work!
136. M<Beating Heart>M<Live Heart> makeBeatingHeartM
M<Beating Heart>Live Heart makeBeatingHeart
Chaining functions
This is what we’ve got:
an M-generating function
This is what we want:
an M-thing only function
137. M<Beating Heart>M<Live Heart> makeBeatingHeartM
M<Beating Heart>Live Heart makeBeatingHeart
Chaining functions
bind
"bind" converts an M-generating function
into a M-thing only function
138. "bind" for M-things
bind
World of M<_> things
Normal World
a
M<b>
World of M<_> things
Normal World
M<a> M<b>
an M-generating function
(diagonal)
139. "bind" for M-things
bind
World of M<_> things
Normal World
a
M<b>
World of M<_> things
Normal World
M<a> M<b>
a pure M-thing function
(horizontal)
141. let makeLiveHeart deadHeart =
let becomeAlive vitalForce =
// snipped
(liveHeart, remainingVitalForce)
M becomeAlive
// signature
// makeLiveHeart : DeadHeart -> M<LiveHeart>
Demo: Chaining
142. let makeBeatingHeart liveHeart =
let becomeAlive vitalForce =
// snipped
(beatingHeart, remainingVitalForce)
M becomeAlive
// signature
// makeBeatingHeart : LiveHeart -> M<BeatingHeart>
Demo: Chaining
143. let beatingHeartM =
// Convert "diagonal" to "horizontal"
let makeBeatingHeartM = bind makeBeatingHeart
Demo: Chaining
144. let beatingHeartM =
// Convert "diagonal" to "horizontal"
let makeBeatingHeartM = bind makeBeatingHeart
// flow the data through each function
DeadHeart "Anne" // DeadHeart
|> makeLiveHeart // output = M<LiveHeart>
|> makeBeatingHeartM // output = M<BeatingHeart>
Demo: Chaining
Q: Where did the vital force tracking go?
A: We are silently threading data
through the code.
But no globals, no mutables!
145. // run the M<BeatingHeart> with some vital force
let beatingHeart, remainingFromHeart =
runM beatingHeartM vf
// val beatingHeart : BeatingHeart =
// BeatingHeart (
// LiveHeart ("Anne",{units = 1}),
// {units = 1} )
//
// val remainingFromHeart : VitalForce =
// {units = 8} // TWO units used up!
Demo: Chaining
Proof that we are silently threading
the vital force through the code!
147. "bind" for all elevated worlds
bind
Elevated World
Normal World
a
E<b>
Elevated World
Normal World
E<a> E<b>
where "elevated world" is
Option, List, Async, etc
148. "bind" for all elevated worlds
bind
Elevated World
Normal World
a
E<b>
Elevated World
Normal World
E<a> E<b>
where "elevated world" is
Option, List, Async, etc
149. The importance of bind
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
"Diagonal" functions
150. The importance of bind
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
Bind
"SelectMany“ in C#
151. The importance of bind
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
Bind
152. The importance of bind
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
Bind
153. The importance of bind
World of normal values
int string bool
World of Lists
List<int> List<string> List<bool>
“Horizontal" functions
155. type LiveBody = {
leftLeg: LiveLeftLeg
rightLeg : LiveLeftLeg
leftArm : LiveLeftHealedArm
rightArm : LiveRightArm
head : LiveHead
heart : BeatingHeart }
Defining the whole body
156. val createBody :
leftLeg :LiveLeftLeg ->
rightLeg :LiveLeftLeg ->
leftArm :LiveLeftHealedArm ->
rightArm :LiveRightArm ->
head :LiveHead ->
beatingHeart :BeatingHeart ->
LiveBody // final result
Creating the whole body
Do we need a "mapSix" function?
160. Introducing "apply"
apply
World of M<_> things
M<(a->b)>
World of M<_> things
M<a> M<b>
apply
M<(a->b->c)> M<a> M<b->c>
apply
M<(a->b->c->d)> M<a> M<b->c->d>
161. Using "apply" to make "map3"
apply
M<(a->b->c->d)> M<a> M<b->c->d>
162. Using "apply" to make "map3"
apply
M<(b->c->d)> M<b> M<c->d>
apply
M<(a->b->c->d)> M<a> M<b->c->d>
163. Using "apply" to make "map3"
apply
M<(b->c->d)> M<b> M<c->d>
M<c->d>
apply
M<c> M<d>
apply
M<(a->b->c->d)> M<a> M<b->c->d>
171. // create the body in the "normal" world
let createBody leftLeg rightLeg leftArm
rightArm head heart =
{
leftLeg = leftLeg
rightLeg = rightLeg
leftArm = leftArm
rightArm = rightArm
head = head
heart = heart
}
Demo: Whole body
172. // <*> means "apply"
let bodyM =
returnM createBody
<*> leftLegM
<*> rightLegM
<*> leftHealedArmM
<*> rightArmM
<*> headM
<*> beatingHeartM
// output is M<LiveBody>
Demo: Whole body
M-Things from
earlier
Output is still a potential thing.
We're "programming"!
176. Live part B
Vital force
Become alive B!
Remaining vital force
Dead part B
Live part A
Vital force
Become alive A!
Remaining vital force
Dead part A
Connect the force between two
steps using "bind" or "apply"
177. Live part B
Vital force
Become alive B!
Remaining vital force
Dead part B
Live part A
Vital force
Become alive A!
Remaining vital force
Dead part A
Combine two outputs
using "map2"
180. The FunctionalToolbox
• "map"
– Lifts functions into the elevated world
• "return"
– Lifts values into the elevated world
• "apply"
– Lets you combine elevated values
– "map2" is a just a specialized "apply“
• "bind"
– Converts “diagonal” functions into horizontal ones
181. The FunctionalToolbox
• "map"
– (with a sensible implementation) is a Functor
• "return" and "apply"
– (with a sensible implementation) is an Applicative
• "return" and "bind"
– (with a sensible implementation) is a Monad
183. let beatingHeartM =
DeadHeart "Anne"
|> makeLiveHeart
|> makeBeatingHeartM
// TWO units of force used up
State monad
Where is the "vital force"
tracking variable?
184. let bodyM =
returnM createBody
<*> leftLegM
<*> rightLegM
<*> leftHealedArmM
<*> rightArmM
<*> headM
<*> beatingHeartM
// EIGHT units of force used up
State monad
Where is the "vital
force" variable?
We are silently threading the vital
force through the code...
...which allows us to focus on the design instead
186. Scenario: Update user information
• Input is {userId, name, email}
• Step 1:Validate input
– Could fail if name is blank, etc
• Step 2: Canonicalize input
– Trim blanks, lowercase email, etc
• Step 3: Fetch existing record from db
– Could fail if record is missing
• Step 4: Update record in db
190. Validate Canonicalize DbFetch DbUpdate
Generates a possible error Generates a possible errorAlways succeeds Doesn't return
How can we glue these
mismatched functions together?
191. World of normal things
"lift" from this world
World of two-track things
to this world
192. World of normal things
World of two-track things
bindmap apply
197. Summary
• We've seen a toolkit of useful techniques
– Don’t expect to understand them all straight away.
• How to wrap a function into a type
– A.k.a. a "computation" or "effect“
• How to use "map", "apply" and "bind"
– Monads are not that scary
– You can work with effects before running them!
• How to thread state "invisibly" through code
– Without using any globals or mutables!