Slide 1

Slide 1 text

Dr Frankenfunctor and the Monadster @ScottWlaschin fsharpforfunandprofit.com/monadster

Slide 2

Slide 2 text

Warning This talk contains: – gruesome topics – strained analogies – discussion of monads Not suitable for sensitive people (seriously)

Slide 3

Slide 3 text

Functional programmers love composition...

Slide 4

Slide 4 text

A function Input Output

Slide 5

Slide 5 text

function A function B Compose

Slide 6

Slide 6 text

function A function B

Slide 7

Slide 7 text

function A and B  Easy!

Slide 8

Slide 8 text

... But here is a challenge for function composition

Slide 9

Slide 9 text

function A Input Output function B Input Output 1 Output 2 function C Input 1 Output 1 Output 2 Input 2 function D Input 1 Output Input 2

Slide 10

Slide 10 text

function A Input Output function B Input Output 1 Output 2 function D Input 1 Output Input 2 How to compose?

Slide 11

Slide 11 text

function A Input Output function B Input Output 1 Output 2 function D Input 1 Output Input 2 How to compose?

Slide 12

Slide 12 text

The answer: monads!

Slide 13

Slide 13 text

The spread of the monad • 1990 ACM Conference on LISP and Functional Programming First monad in captivity

Slide 14

Slide 14 text

The terrible events at the 1990 ACM Conference on LISP and Functional Programming

Slide 15

Slide 15 text

The spread of the monad • 1990 ACM Conference on LISP and Functional Programming • 1991 Eugenio Moggi, "Notions of computation and monads" • 1992 Philip Wadler, "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

Slide 16

Slide 16 text

The spread of the monad • 1990 ACM Conference on LISP and Functional Programming • 1991 Eugenio Moggi, "Notions of computation and monads" • 1992 Philip Wadler, "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

Slide 17

Slide 17 text

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 Philip Wadler, "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

Slide 18

Slide 18 text

The story of the Monadster

Slide 19

Slide 19 text

The creature was built from body parts of various shapes

Slide 20

Slide 20 text

The various parts were assembled into a whole

Slide 21

Slide 21 text

The body was animated in a single instant, using a bolt of lightning to create the vital force.

Slide 22

Slide 22 text

... The Monadster The “mark of the lambda”

Slide 23

Slide 23 text

But how was it done? I have devoted many years of research into this matter...

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Technique 1: Modelling with pure functions

Slide 27

Slide 27 text

Become alive! Vital force Dead part Live part Don't try this at home

Slide 28

Slide 28 text

Live body part Vital force Become alive! Remaining vital force Dead body part Two inputs

Slide 29

Slide 29 text

Live body part Vital force Become alive! Remaining vital force Dead body part Two outputs

Slide 30

Slide 30 text

Live body part Vital force Become alive! Remaining vital force Dead body part Less vital force available afterwards

Slide 31

Slide 31 text

Live body part Vital force Become alive! Remaining vital force Dead body part No globals, no mutation!

Slide 32

Slide 32 text

But now you have two problems...

Slide 33

Slide 33 text

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?

Slide 34

Slide 34 text

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?

Slide 35

Slide 35 text

Technique 2: Wrapping the "Become Alive" function Also, introducing schönfinkelling

Slide 36

Slide 36 text

Moses Schönfinkel invented schönfinkelling

Slide 37

Slide 37 text

Moses Schönfinkel invented schönfinkelling

Slide 38

Slide 38 text

Haskell Curry gave his name to currying

Slide 39

Slide 39 text

Input A Uncurried Function Input B Output C Curried Function Input A Intermediate Function Output C Input B What is currying? after currying Currying means that *every* function has one input

Slide 40

Slide 40 text

// naming a lambda Func add1 = (y => 1 + y) // using it var three = add1(2) Currying examples

Slide 41

Slide 41 text

// naming a lambda let add1 = (fun y -> 1 + y) // using it let three = add1 2 Currying examples

Slide 42

Slide 42 text

// 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

Slide 43

Slide 43 text

// "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

Slide 44

Slide 44 text

// removing the parens let three = add 1 2 Currying examples // returning a lambda with baked in "x" let add x = (fun y -> x + y)

Slide 45

Slide 45 text

Live part A Vital force Become alive! Remaining vital force Dead part A Currying "become alive!"

Slide 46

Slide 46 text

Become alive! Dead part A Vital force Live part A Currying "become alive!"

Slide 47

Slide 47 text

Become alive! Dead part A Vital force M Live part A Wrapping the function "M" is for "Monadster"

Slide 48

Slide 48 text

Become alive! Dead part A Vital force M Live part A Wrapping the function

Slide 49

Slide 49 text

Dead part A M Create step in recipe Wrapping the function An "M-making" function Remember -- this is *not* a live part , it's a "potential" live part

Slide 50

Slide 50 text

M Run Live part A Remaining vital force Running the function Vital force

Slide 51

Slide 51 text

M Run Live part A Remaining vital force Running the function Vital force Become alive! Vital force Live part A M

Slide 52

Slide 52 text

Show me the code Left Leg

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

// create DeadLeftLeg let deadLeftLeg = DeadLeftLeg "Boris" // create a M 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

Slide 61

Slide 61 text

Technique 3: Transforming live parts

Slide 62

Slide 62 text

A Broken Arm Dead Broken Arm What we've got Live Healed Arm What we want

Slide 63

Slide 63 text

Healing a broken arm Live Healed Arm Live Broken Arm HealBrokenArm We have this function!

Slide 64

Slide 64 text

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...

Slide 65

Slide 65 text

Create Dead Broken Arm Dead Healed Arm Live Healed Arm Healing a broken arm HealBrokenArm

Slide 66

Slide 66 text

Create Dead Broken Arm Dead Healed Arm Live Healed Arm  No. We can only heal live arms Healing a broken arm HealBrokenArm

Slide 67

Slide 67 text

Dead Broken Arm Live Healed Arm Live Broken Arm Create HealBrokenArm Healing a broken arm

Slide 68

Slide 68 text

Dead Broken Arm Live Healed Arm Live Broken Arm Create HealBrokenArm No. We can't create live things directly, only M-type things  Healing a broken arm 

Slide 69

Slide 69 text

Dead Broken Arm M M Create HealBrokenArm Healing a broken arm

Slide 70

Slide 70 text

Dead Broken Arm M  M Create HealBrokenArm No. "HealBrokenArm" doesn't work on M-type things  Healing a broken arm

Slide 71

Slide 71 text

Dead Broken Arm M M Create HealBrokenArmM We need a special "HealBrokenArmM" that works on M-type things   Where can we get it from? Healing a broken arm

Slide 72

Slide 72 text

Live Healed Arm Live Broken Arm HealBrokenArm Healing a broken arm M M HealBrokenArmM This is what we’ve got This is what we want

Slide 73

Slide 73 text

Live Healed Arm Live Broken Arm HealBrokenArm Healing a broken arm M M HealBrokenArmM map

Slide 74

Slide 74 text

"map" is generic for M-things Normal World a b

Slide 75

Slide 75 text

"map" is generic for M-things Normal World a b map

Slide 76

Slide 76 text

"map" is generic for M-things Normal World a b map World of M<_> things M M A function in the world of M-things “lifting”

Slide 77

Slide 77 text

Show me the code Broken Arm and "map"

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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.

Slide 84

Slide 84 text

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 // run the M with some vital force let liveLeftHealedArm, remainingAfterLeftArm = runM leftHealedArmM vf Demo – Broken Arm

Slide 85

Slide 85 text

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 // run the M with some vital force let liveLeftHealedArm, remainingAfterLeftArm = runM leftHealedArmM vf Demo – Broken Arm

Slide 86

Slide 86 text

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 // run the M with some vital force let liveLeftHealedArm, remainingAfterLeftArm = runM leftHealedArmM vf Demo – Broken Arm // output // liveLeftHealedArm : LiveLeftHealedArm = // LiveLeftHealedArm ("Victor",{units = 1}) // remainingAfterLeftArm : VitalForce = // {units = 9}

Slide 87

Slide 87 text

The importance of map

Slide 88

Slide 88 text

The "map" pattern for elevated worlds map Elevated World Normal World a b Elevated World Normal World E E A function in the world of normal things where "elevated world" is Option, List, Async, etc

Slide 89

Slide 89 text

The "map" pattern for elevated worlds map Elevated World Normal World a b Elevated World Normal World E E A function in the world of E-things where "elevated world" is Option, List, Async, etc

Slide 90

Slide 90 text

The importance of map World of normal values int string bool World of Lists List List List

Slide 91

Slide 91 text

The importance of map  World of normal values int string bool World of Lists List List List

Slide 92

Slide 92 text

The importance of map  World of normal values int string bool World of Lists List List List

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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 

Slide 96

Slide 96 text

How not to code with lists addTwo  World of normal values World of Lists

Slide 97

Slide 97 text

How to code with lists addTwo_L World of normal values World of Lists

Slide 98

Slide 98 text

How to code with lists T -> U List -> List List.map World of normal values World of Lists Linq.Select

Slide 99

Slide 99 text

How to code with lists addTwo addTwo_L [1;2] |> 1 |> // 3 // [3;4] World of normal values World of Lists

Slide 100

Slide 100 text

// map works with "addTwo" let addTwo_L = List.map addTwo // List -> List addTwo_L [1;2] // List = [3; 4] [1;2] |> List.map addOne // List = [3; 4] // map works with "healBrokenArm" let healBrokenArm_L = List.map healBrokenArm // List -> List Same applies for any generic type: Option, Task, etc

Slide 101

Slide 101 text

Technique 4: Combining two live parts

Slide 102

Slide 102 text

Combining two parts Dead Lower Arm Dead Upper Arm Live Whole Arm What we've got What we want

Slide 103

Slide 103 text

Combining two parts Live Arm Live Lower Arm ArmSurgery Live Whole Arm Live Upper Arm We have this function!

Slide 104

Slide 104 text

Live Arm ...and we want one of these! How can we get it? Combining two parts Dead Lower Arm We have these... Dead Upper Arm

Slide 105

Slide 105 text

Live Arm Combining two parts Dead Lower Arm Dead Upper Arm Dead Arm Create ArmSurgery

Slide 106

Slide 106 text

Live Arm Combining two parts Dead Lower Arm Dead Upper Arm Dead Arm Create  No. Only works on live arms ArmSurgery

Slide 107

Slide 107 text

Create ArmSurgery Combining two parts Dead Lower Arm Dead Upper Arm Live Lower Arm Live Upper Arm Live Arm Create

Slide 108

Slide 108 text

 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

Slide 109

Slide 109 text

 Create ArmSurgery  Combining two parts Dead Lower Arm Dead Upper Arm M M M Create No. "ArmSurgery" doesn't work on M-type things 

Slide 110

Slide 110 text

Create ArmSurgeryM  Combining two parts Dead Lower Arm Dead Upper Arm M M M Create We need a special "ArmSurgeryM" that works on M-type things  

Slide 111

Slide 111 text

Combining two parts map2 ArmSurgery Live Lower Arm Live Upper Arm Live Arm ArmSurgeryM M M M

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

A 2-param function in the world of Ms World of things World of M<_> things Param1 -> Param2 -> Result M -> M -> M map2 The "map2" pattern for M-things

Slide 114

Slide 114 text

A 2-param function in the world of Es World of things World of E<_> things Param1 -> Param2 -> Result E -> E -> E map2 The "map2" pattern for elevated worlds Applies to any generic type: Option, Task, etc

Slide 115

Slide 115 text

Technique 5: Combining live and dead parts

Slide 116

Slide 116 text

Combining mismatched parts Empty Head Dead Brain What we've got Live Head What we want

Slide 117

Slide 117 text

Combining mismatched parts Live Head Live Brain HeadSurgery Empty Head Combining function alive not alive

Slide 118

Slide 118 text

Create HeadSurgery Combining mismatched parts Dead Brain Empty Head Live Brain Empty Head Live Head Copy

Slide 119

Slide 119 text

Create HeadSurgery Combining mismatched parts Dead Brain Empty Head Live Brain Empty Head Live Head Copy  No. We can't make live things directly, only M-type things

Slide 120

Slide 120 text

Create HeadSurgeryM  Combining mismatched parts Dead Brain Empty Head M M M

Slide 121

Slide 121 text

Create HeadSurgeryM  Combining mismatched parts Dead Brain Empty Head M M So what goes here? M This is not a live thing

Slide 122

Slide 122 text

Combining mismatched parts return Anything M

Slide 123

Slide 123 text

Create HeadSurgeryM  Combining mismatched parts Dead Brain M M M return Empty Head

Slide 124

Slide 124 text

Create HeadSurgeryM  Combining mismatched parts Dead Brain M M M return Empty Head Both are M-things now

Slide 125

Slide 125 text

Create HeadSurgeryM  Combining mismatched parts Dead Brain M M M map2 Empty Head Live Head Live Brain HeadSurgery Empty Head return

Slide 126

Slide 126 text

"return" for M-things return Normal World a World of M<_> things A value in the world of normal things

Slide 127

Slide 127 text

"return" for M-things return Normal World a World of M<_> things M A value in the world of M-things

Slide 128

Slide 128 text

"return" for all elevated worlds return Normal World a Elevated World E A value in the world of normal things A value in the world of E-things

Slide 129

Slide 129 text

Technique 6: Chaining M-generating functions

Slide 130

Slide 130 text

Chaining functions Beating Heart Dead Heart What we want What we've got

Slide 131

Slide 131 text

Chaining functions Beating Heart Live Heart Dead Heart Creating a beating heart is a two-step process

Slide 132

Slide 132 text

Chaining functions Dead Heart M Live Heart M We have an M-generating function We have another M-generating function

Slide 133

Slide 133 text

Dead Heart M  Live Heart M   Chaining functions Output type doesn't match input type

Slide 134

Slide 134 text

Dead Heart M  Live Heart M  Chaining functions 

Slide 135

Slide 135 text

Dead Heart M  M M  Chaining functions If we could change this type to M, it would work! 

Slide 136

Slide 136 text

M M makeBeatingHeartM M 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

Slide 137

Slide 137 text

M M makeBeatingHeartM M Live Heart makeBeatingHeart Chaining functions bind "bind" converts an M-generating function into a M-thing only function

Slide 138

Slide 138 text

"bind" for M-things bind World of M<_> things Normal World a M World of M<_> things Normal World M M an M-generating function (diagonal)

Slide 139

Slide 139 text

"bind" for M-things bind World of M<_> things Normal World a M World of M<_> things Normal World M M a pure M-thing function (horizontal)

Slide 140

Slide 140 text

Show me the code Beating Heart and "bind"

Slide 141

Slide 141 text

let makeLiveHeart deadHeart = let becomeAlive vitalForce = // snipped (liveHeart, remainingVitalForce) M becomeAlive // signature // makeLiveHeart : DeadHeart -> M Demo: Chaining

Slide 142

Slide 142 text

let makeBeatingHeart liveHeart = let becomeAlive vitalForce = // snipped (beatingHeart, remainingVitalForce) M becomeAlive // signature // makeBeatingHeart : LiveHeart -> M Demo: Chaining

Slide 143

Slide 143 text

let beatingHeartM = // Convert "diagonal" to "horizontal" let makeBeatingHeartM = bind makeBeatingHeart Demo: Chaining

Slide 144

Slide 144 text

let beatingHeartM = // Convert "diagonal" to "horizontal" let makeBeatingHeartM = bind makeBeatingHeart // flow the data through each function DeadHeart "Anne" // DeadHeart |> makeLiveHeart // output = M |> makeBeatingHeartM // output = M Demo: Chaining Q: Where did the vital force tracking go? A: We are silently threading data through the code. But no globals, no mutables!

Slide 145

Slide 145 text

// run the M 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!

Slide 146

Slide 146 text

The importance of bind

Slide 147

Slide 147 text

"bind" for all elevated worlds bind Elevated World Normal World a E Elevated World Normal World E E where "elevated world" is Option, List, Async, etc

Slide 148

Slide 148 text

"bind" for all elevated worlds bind Elevated World Normal World a E Elevated World Normal World E E where "elevated world" is Option, List, Async, etc

Slide 149

Slide 149 text

The importance of bind World of normal values int string bool World of Lists List List List "Diagonal" functions

Slide 150

Slide 150 text

The importance of bind World of normal values int string bool World of Lists List List List Bind "SelectMany“ in C#

Slide 151

Slide 151 text

The importance of bind World of normal values int string bool World of Lists List List List Bind

Slide 152

Slide 152 text

The importance of bind World of normal values int string bool World of Lists List List List Bind

Slide 153

Slide 153 text

The importance of bind  World of normal values int string bool World of Lists List List List “Horizontal" functions

Slide 154

Slide 154 text

Technique 7: Lifting arbitrary functions Making "map3", "map4", "map5" on the fly

Slide 155

Slide 155 text

type LiveBody = { leftLeg: LiveLeftLeg rightLeg : LiveLeftLeg leftArm : LiveLeftHealedArm rightArm : LiveRightArm head : LiveHead heart : BeatingHeart } Defining the whole body

Slide 156

Slide 156 text

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?

Slide 157

Slide 157 text

Introducing "apply" apply World of M<_> things M<(a->b)> World of M<_> things

Slide 158

Slide 158 text

Introducing "apply" apply World of M<_> things M<(a->b)> World of M<_> things M M

Slide 159

Slide 159 text

Introducing "apply" apply World of M<_> things M<(a->b)> World of M<_> things M M apply M<(a->b->c)> M Mc>

Slide 160

Slide 160 text

Introducing "apply" apply World of M<_> things M<(a->b)> World of M<_> things M M apply M<(a->b->c)> M Mc> apply M<(a->b->c->d)> M Mc->d>

Slide 161

Slide 161 text

Using "apply" to make "map3" apply M<(a->b->c->d)> M Mc->d>

Slide 162

Slide 162 text

Using "apply" to make "map3" apply M<(b->c->d)> M Md> apply M<(a->b->c->d)> M Mc->d>

Slide 163

Slide 163 text

Using "apply" to make "map3" apply M<(b->c->d)> M Md> Md> apply M M apply M<(a->b->c->d)> M Mc->d>

Slide 164

Slide 164 text

Using "apply" to make "map3" a->b->c->Result a b c Result

Slide 165

Slide 165 text

Using "apply" to make "map3" M<(a->b->c->d)> a->b->c->Result a b c Result return

Slide 166

Slide 166 text

Using "apply" to make "map3" apply M<(a->b->c->d)> M a->b->c->Result a b c Result return create

Slide 167

Slide 167 text

Using "apply" to make "map3" apply M<(a->b->c->d)> M M a->b->c->Result a b c Result return create create apply

Slide 168

Slide 168 text

Using "apply" to make "map3" apply M<(a->b->c->d)> M M M a->b->c->Result a b c Result return create create create apply apply

Slide 169

Slide 169 text

Using "apply" to make "map3" apply M<(a->b->c->d)> M M M M a->b->c->Result a b c Result return create create create apply apply

Slide 170

Slide 170 text

Show me the code Whole body and "apply"

Slide 171

Slide 171 text

// 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

Slide 172

Slide 172 text

// <*> means "apply" let bodyM = returnM createBody <*> leftLegM <*> rightLegM <*> leftHealedArmM <*> rightArmM <*> headM <*> beatingHeartM // output is M Demo: Whole body M-Things from earlier Output is still a potential thing. We're "programming"!

Slide 173

Slide 173 text

// Lightning strikes! It's alive! let liveBody, remainingFromBody = runM bodyM vf // val liveBody : LiveBody = // {leftLeg = LiveLeftLeg ("Boris",{units = 1}) // rightLeg = LiveLeftLeg ("Boris",{units = 1}) // leftArm = LiveLeftArm ("Victor",{units = 1}) // rightArm = {lowerArm = LiveRightLowerArm // ("Tom",{units = 1}) // upperArm = LiveRightUpperArm // ("Jerry",{units = 1}) } // head = {brain = LiveBrain // ("Abby Normal",{units = 1}) // emptyHead = EmptyHead "Yorick"} // heart = BeatingHeart ( // LiveHeart ("Anne",{units = 1}), // {units = 1})} // val remainingFromBody : VitalForce = {units = 2} Demo: Whole body The state is automatically kept up-to-date

Slide 174

Slide 174 text

Is your brain hurting now?

Slide 175

Slide 175 text

Do we still have two problems?

Slide 176

Slide 176 text

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"

Slide 177

Slide 177 text

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"

Slide 178

Slide 178 text

A Functional Toolbox

Slide 179

Slide 179 text

map return bind map2 apply

Slide 180

Slide 180 text

The Functional Toolbox • "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

Slide 181

Slide 181 text

The Functional Toolbox • "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

Slide 182

Slide 182 text

The State monad The state is threaded through the code "invisibly"

Slide 183

Slide 183 text

let beatingHeartM = DeadHeart "Anne" |> makeLiveHeart |> makeBeatingHeartM // TWO units of force used up State monad Where is the "vital force" tracking variable?

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

Using Dr Frankenfunctor's techniques in the real world Is this too academic? Too abstract to be useful?

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

Validate Generates a possible error

Slide 188

Slide 188 text

Validate Canonicalize Generates a possible error Always succeeds

Slide 189

Slide 189 text

Validate Canonicalize DbFetch Generates a possible error Generates a possible error Always succeeds

Slide 190

Slide 190 text

Validate Canonicalize DbFetch DbUpdate Generates a possible error Generates a possible error Always succeeds Doesn't return How can we glue these mismatched functions together?

Slide 191

Slide 191 text

World of normal things "lift" from this world World of two-track things to this world

Slide 192

Slide 192 text

World of normal things World of two-track things bind map apply

Slide 193

Slide 193 text

map Converting everything to two-track

Slide 194

Slide 194 text

bind map Converting everything to two-track

Slide 195

Slide 195 text

bind map tee map Converting everything to two-track

Slide 196

Slide 196 text

Validate Canonicalize DbFetch DbUpdate Now we *can* glue these together easily! map bind tee, then map

Slide 197

Slide 197 text

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!

Slide 198

Slide 198 text

Thanks! @ScottWlaschin fsharpforfunandprofit.com/monadster Contact me Slides and video here Let us know if you need help with F#