$30 off During Our Annual Pro Sale. View Details »

Dr Frankenfunctor and the Monadster

Dr Frankenfunctor and the Monadster

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

Scott Wlaschin

October 15, 2015
Tweet

More Decks by Scott Wlaschin

Other Decks in Programming

Transcript

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

  2. Warning This talk contains: – gruesome topics – strained analogies

    – discussion of monads Not suitable for sensitive people (seriously)
  3. Functional programmers love composition...

  4. A function Input Output

  5. function A function B Compose

  6. function A function B

  7. function A and B  Easy!

  8. ... But here is a challenge for function composition

  9. 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
  10. function A Input Output function B Input Output 1 Output

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

    2 function D Input 1 Output Input 2 How to compose?
  12. The answer: monads!

  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 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
  16. 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
  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 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
  18. The story of the Monadster

  19. The creature was built from body parts of various shapes

  20. The various parts were assembled into a whole

  21. The body was animated in a single instant, using a

    bolt of lightning to create the vital force.
  22. ... The Monadster The “mark of the lambda”

  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
  26. Technique 1: Modelling with pure functions

  27. Become alive! Vital force Dead part Live part Don't try

    this at home
  28. Live body part Vital force Become alive! Remaining vital force

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

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

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

    Dead body part No globals, no mutation!
  32. But now you have two problems...

  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?
  35. Technique 2: Wrapping the "Become Alive" function Also, introducing schönfinkelling

  36. Moses Schönfinkel invented schönfinkelling

  37. Moses Schönfinkel invented schönfinkelling

  38. Haskell Curry gave his name to currying

  39. 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
  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!"
  46. Become alive! Dead part A Vital force Live part A

    Currying "become alive!"
  47. Become alive! Dead part A Vital force M<Live Part A>

    Live part A Wrapping the function "M" is for "Monadster"
  48. Become alive! Dead part A Vital force M<Live Part A>

    Live part A Wrapping the function
  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>
  52. Show me the code Left Leg

  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
  61. Technique 3: Transforming live parts

  62. A Broken Arm Dead Broken Arm What we've got Live

    Healed Arm What we want
  63. Healing a broken arm Live Healed Arm Live 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 Arm Live Broken Arm Create

    HealBrokenArm Healing a broken arm
  68. 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 
  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 Arm Live 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 Arm Live Broken Arm HealBrokenArm Healing a broken

    arm M<Live Healed Arm> M<Live Broken Arm> HealBrokenArmM map
  74. "map" is generic for M-things Normal World a b

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

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

    World of M<_> things M<a> M<b> A function in the world of M-things “lifting”
  77. Show me the code Broken Arm and "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}
  87. The importance of map

  88. The "map" pattern for elevated worlds map Elevated World Normal

    World a b Elevated World 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 Elevated World Normal

    World a b Elevated World 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
  101. Technique 4: Combining two live parts

  102. Combining two parts Dead Lower Arm Dead Upper Arm Live

    Whole Arm What we've got What we want
  103. Combining two parts Live Arm Live Lower Arm ArmSurgery Live

    Whole Arm Live Upper Arm We have this function!
  104. 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
  105. Live Arm Combining two parts Dead Lower Arm Dead Upper

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

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

    Arm Live Lower Arm Live Upper Arm Live Arm Create
  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
  109.  Create ArmSurgery  Combining two parts Dead Lower Arm

    Dead Upper Arm M<LiveLowerArm> M<LiveUpperArm> M<LiveArm> Create No. "ArmSurgery" doesn't work on M-type things 
  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  
  111. Combining two parts map2 ArmSurgery Live Lower Arm Live Upper

    Arm Live Arm ArmSurgeryM M<LiveLowerArm> M<LiveUpperArm> M<LiveArm>
  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
  115. Technique 5: Combining live and dead parts

  116. Combining mismatched parts Empty Head Dead Brain What we've got

    Live Head What we want
  117. Combining mismatched parts Live Head Live Brain HeadSurgery Empty Head

    Combining function alive not alive
  118. Create HeadSurgery Combining mismatched parts Dead Brain Empty Head Live

    Brain Empty Head Live Head Copy
  119. 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
  120. Create HeadSurgeryM  Combining mismatched parts Dead Brain Empty Head

    M<Live Brain> M<Empty Head> M<Live Head>
  121. Create HeadSurgeryM  Combining mismatched parts Dead Brain Empty Head

    M<Live Brain> M<Empty Head> So what goes here? M<Live Head> This is not a live thing
  122. Combining mismatched parts return Anything M<Anything>

  123. Create HeadSurgeryM  Combining mismatched parts Dead Brain M<Live Brain>

    M<Empty Head> M<Live Head> return Empty Head
  124. Create HeadSurgeryM  Combining mismatched parts Dead Brain M<Live Brain>

    M<Empty Head> M<Live Head> return Empty Head Both are M-things now
  125. Create HeadSurgeryM  Combining mismatched parts Dead Brain M<Live Brain>

    M<Empty Head> M<Live Head> map2 Empty Head Live Head Live Brain HeadSurgery Empty Head return
  126. "return" for M-things return Normal World a World of M<_>

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

    things M<a> A value in the world of M-things
  128. "return" for all elevated worlds return Normal World a Elevated

    World E<a> A value in the world of normal things A value in the world of E-things
  129. Technique 6: Chaining M-generating functions

  130. Chaining functions Beating Heart Dead Heart What we want What

    we've got
  131. Chaining functions Beating Heart Live Heart Dead Heart Creating a

    beating heart is a two-step process
  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)
  140. Show me the code Beating Heart and "bind"

  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!
  146. The importance of bind

  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
  154. Technique 7: Lifting arbitrary functions Making "map3", "map4", "map5" on

    the fly
  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?
  157. Introducing "apply" apply World of M<_> things M<(a->b)> World of

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

    M<_> things M<a> M<b>
  159. 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>
  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>
  164. Using "apply" to make "map3" a->b->c->Result a b c Result

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

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

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

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

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

    M<Result> a->b->c->Result a b c Result return create create create apply apply
  170. Show me the code Whole body and "apply"

  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"!
  173. // 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
  174. Is your brain hurting now?

  175. Do we still have two problems?

  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"
  178. A Functional Toolbox

  179. map return bind map2 apply

  180. 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
  181. 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
  182. The State monad The state is threaded through the code

    "invisibly"
  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
  185. Using Dr Frankenfunctor's techniques in the real world Is this

    too academic? Too abstract to be useful?
  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
  187. Validate Generates a possible error

  188. Validate Canonicalize Generates a possible error Always succeeds

  189. Validate Canonicalize DbFetch Generates a possible error Generates a possible

    error Always succeeds
  190. 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?
  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 bind map

    apply
  193. map Converting everything to two-track

  194. bind map Converting everything to two-track

  195. bind map tee map Converting everything to two-track

  196. Validate Canonicalize DbFetch DbUpdate Now we *can* glue these together

    easily! map bind tee, then map
  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!
  198. Thanks! @ScottWlaschin fsharpforfunandprofit.com/monadster Contact me Slides and video here Let

    us know if you need help with F#