$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

    View Slide

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

    View Slide

  3. Functional programmers
    love composition...

    View Slide

  4. A function
    Input Output

    View Slide

  5. function A function B
    Compose

    View Slide

  6. function A function B

    View Slide

  7. function A and B

    Easy!

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  12. The answer: monads!

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  18. The story of the Monadster

    View Slide

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

    View Slide

  20. The various parts were assembled into a whole

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  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

    View Slide

  26. Technique 1:
    Modelling with pure functions

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. But now you have two
    problems...

    View Slide

  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?

    View Slide

  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?

    View Slide

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

    View Slide

  36. Moses Schönfinkel
    invented schönfinkelling

    View Slide

  37. Moses Schönfinkel
    invented schönfinkelling

    View Slide

  38. Haskell Curry
    gave his name to currying

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. Show me the code
    Left Leg

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  61. Technique 3:
    Transforming live parts

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. Create
    Dead Broken Arm Dead Healed Arm
    Live Healed Arm

    No. We can only heal live arms
    Healing a broken arm
    HealBrokenArm

    View Slide

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

    View Slide

  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

    View Slide

  69. Dead Broken Arm
    M
    M
    Create
    HealBrokenArm
    Healing a broken arm

    View Slide

  70. Dead Broken Arm
    M

    M
    Create
    HealBrokenArm
    No. "HealBrokenArm" doesn't
    work on M-type things

    Healing a broken arm

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. "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”

    View Slide

  77. Show me the code
    Broken Arm and "map"

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  87. The importance of map

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  91. The importance of map

    World of normal values
    int string bool
    World of Lists
    List List List

    View Slide

  92. The importance of map

    World of normal values
    int string bool
    World of Lists
    List List List

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  96. How not to code with lists
    addTwo

    World of normal values
    World of Lists

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  101. Technique 4:
    Combining two live parts

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    No. Only works on live arms
    ArmSurgery

    View Slide

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

    View Slide


  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

    View Slide


  109. Create
    ArmSurgery

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

    View Slide

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


    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  115. Technique 5:
    Combining live and dead parts

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  120. Create
    HeadSurgeryM

    Combining mismatched parts
    Dead Brain Empty Head
    M M M

    View Slide

  121. Create
    HeadSurgeryM

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

    View Slide

  122. Combining mismatched parts
    return
    Anything
    M

    View Slide

  123. Create
    HeadSurgeryM

    Combining mismatched parts
    Dead Brain
    M M M
    return
    Empty Head

    View Slide

  124. Create
    HeadSurgeryM

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

    View Slide

  125. Create
    HeadSurgeryM

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  129. Technique 6:
    Chaining M-generating functions

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  133. Dead Heart M

    Live Heart M


    Chaining functions
    Output type doesn't
    match input type

    View Slide

  134. Dead Heart M

    Live Heart M

    Chaining functions

    View Slide

  135. Dead Heart M

    M M

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

    View Slide

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

    View Slide

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

    View Slide

  138. "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)

    View Slide

  139. "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)

    View Slide

  140. Show me the code
    Beating Heart and "bind"

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  145. // 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!

    View Slide

  146. The importance of bind

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  153. The importance of bind

    World of normal values
    int string bool
    World of Lists
    List List List
    “Horizontal" functions

    View Slide

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

    View Slide

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

    View Slide

  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?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  170. Show me the code
    Whole body and "apply"

    View Slide

  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

    View Slide

  172. // <*> 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"!

    View Slide

  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

    View Slide

  174. Is your brain hurting now?

    View Slide

  175. Do we still have two problems?

    View Slide

  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"

    View Slide

  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"

    View Slide

  178. A Functional Toolbox

    View Slide

  179. map
    return
    bind
    map2
    apply

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  187. Validate
    Generates a possible error

    View Slide

  188. Validate Canonicalize
    Generates a possible error Always succeeds

    View Slide

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

    View Slide

  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?

    View Slide

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

    View Slide

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

    View Slide

  193. map
    Converting everything to two-track

    View Slide

  194. bind
    map
    Converting everything to two-track

    View Slide

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

    View Slide

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

    View Slide

  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!

    View Slide

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

    View Slide