Dr Frankenfunctor and the Monadster

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

Transcript of Dr Frankenfunctor and the Monadster

Page 1: Dr Frankenfunctor and the Monadster

Dr Frankenfunctor and

the Monadster

@ScottWlaschin

fsharpforfunandprofit.com/monadster

Page 2: Dr Frankenfunctor and the Monadster

Warning

This talk contains:

– gruesome topics

– strained analogies

– discussion of monads

Not suitable for sensitive people (seriously)

Page 3: Dr Frankenfunctor and the Monadster

Functional programmers

love composition...

Page 4: Dr Frankenfunctor and the Monadster

A function Input Output

Page 5: Dr Frankenfunctor and the Monadster

function A function B Compose

Page 6: Dr Frankenfunctor and the Monadster

function A function B

Page 7: Dr Frankenfunctor and the Monadster

function A and B

Easy!

Page 8: Dr Frankenfunctor and the Monadster

... But here is a challenge for

function composition

Page 9: Dr Frankenfunctor and the Monadster

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

Page 10: Dr Frankenfunctor and the Monadster

function A Input Output

function B Input Output 1

Output 2

function D Input 1 Output Input 2

How to compose?

Page 11: Dr Frankenfunctor and the Monadster

function A Input Output

function B Input Output 1

Output 2

function D Input 1 Output Input 2

How to compose?

Page 12: Dr Frankenfunctor and the Monadster

The answer: monads!

Page 13: Dr Frankenfunctor and the Monadster

The spread of the monad

• 1990 ACM Conference on LISP and Functional Programming

First monad in captivity

Page 14: Dr Frankenfunctor and the Monadster

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

Page 15: Dr Frankenfunctor and the Monadster

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

Page 16: Dr Frankenfunctor and the Monadster

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

Page 17: Dr Frankenfunctor and the Monadster

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

Page 18: Dr Frankenfunctor and the Monadster

The story of the Monadster

Page 19: Dr Frankenfunctor and the Monadster

The creature was built from body parts of various shapes

Page 20: Dr Frankenfunctor and the Monadster

The various parts were assembled into a whole

Page 21: Dr Frankenfunctor and the Monadster

The body was animated in a single instant, using a

bolt of lightning to create the vital force.

Page 22: Dr Frankenfunctor and the Monadster

... The Monadster

The “mark of the lambda”

Page 23: Dr Frankenfunctor and the Monadster

But how was it done?

I have devoted many years of

research into this matter...

Page 24: Dr Frankenfunctor and the Monadster

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.

Page 25: Dr Frankenfunctor and the Monadster

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

Page 26: Dr Frankenfunctor and the Monadster

Technique 1: Modelling with pure functions

Page 27: Dr Frankenfunctor and the Monadster

Become alive!

Vital force

Dead part Live part

Don't try this at home

Page 28: Dr Frankenfunctor and the Monadster

Live body part

Vital force

Become alive!

Remaining vital force

Dead body part

Two inputs

Page 29: Dr Frankenfunctor and the Monadster

Live body part

Vital force

Become alive!

Remaining vital force

Dead body part

Two outputs

Page 30: Dr Frankenfunctor and the Monadster

Live body part

Vital force

Become alive!

Remaining vital force

Dead body part

Less vital force available afterwards

Page 31: Dr Frankenfunctor and the Monadster

Live body part

Vital force

Become alive!

Remaining vital force

Dead body part

No globals, no mutation!

Page 32: Dr Frankenfunctor and the Monadster

But now you have two

problems...

Page 33: Dr Frankenfunctor and the Monadster

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?

Page 34: Dr Frankenfunctor and the Monadster

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?

Page 35: Dr Frankenfunctor and the Monadster

Technique 2: Wrapping the "Become Alive" function

Also, introducing schönfinkelling

Page 36: Dr Frankenfunctor and the Monadster

Moses Schönfinkel

invented schönfinkelling

Page 37: Dr Frankenfunctor and the Monadster

Moses Schönfinkel

invented schönfinkelling

Page 38: Dr Frankenfunctor and the Monadster

Haskell Curry

gave his name to currying

Page 39: Dr Frankenfunctor and the Monadster

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

Page 40: Dr Frankenfunctor and the Monadster

// naming a lambda Func<int,int> add1 = (y => 1 + y) // using it var three = add1(2)

Currying examples

Page 41: Dr Frankenfunctor and the Monadster

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

Currying examples

Page 42: Dr Frankenfunctor and the Monadster

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

Page 43: Dr Frankenfunctor and the Monadster

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

Page 44: Dr Frankenfunctor and the Monadster

// removing the parens let three = add 1 2

Currying examples

// returning a lambda with baked in "x" let add x = (fun y -> x + y)

Page 45: Dr Frankenfunctor and the Monadster

Live part A

Vital force

Become alive!

Remaining vital force

Dead part A

Currying "become alive!"

Page 46: Dr Frankenfunctor and the Monadster

Become alive!

Dead part A Vital force

Live part A

Currying "become alive!"

Page 47: Dr Frankenfunctor and the Monadster

Become alive!

Dead part A Vital force

M<Live Part A>

Live part A

Wrapping the function

"M" is for "Monadster"

Page 48: Dr Frankenfunctor and the Monadster

Become alive!

Dead part A Vital force

M<Live Part A>

Live part A

Wrapping the function

Page 49: Dr Frankenfunctor and the Monadster

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

Page 50: Dr Frankenfunctor and the Monadster

M<Live Part A> Run

Live part A

Remaining vital force

Running the function

Vital force

Page 51: Dr Frankenfunctor and the Monadster

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>

Page 52: Dr Frankenfunctor and the Monadster

Show me the code

Left Leg

Page 53: Dr Frankenfunctor and the Monadster

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

Page 54: Dr Frankenfunctor and the Monadster

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

Page 55: Dr Frankenfunctor and the Monadster

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

Page 56: Dr Frankenfunctor and the Monadster

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

Page 57: Dr Frankenfunctor and the Monadster

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

Page 58: Dr Frankenfunctor and the Monadster

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

Page 59: Dr Frankenfunctor and the Monadster

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>

Page 60: Dr Frankenfunctor and the Monadster

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

Page 61: Dr Frankenfunctor and the Monadster

Technique 3:

Transforming live parts

Page 62: Dr Frankenfunctor and the Monadster

A Broken Arm

Dead Broken Arm

What we've got

Live Healed Arm

What we want

Page 63: Dr Frankenfunctor and the Monadster

Healing a broken arm

Live Healed Arm Live Broken Arm HealBrokenArm

We have this function!

Page 64: Dr Frankenfunctor and the Monadster

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

Page 65: Dr Frankenfunctor and the Monadster

Create

Dead Broken Arm Dead Healed Arm

Live Healed Arm

Healing a broken arm

HealBrokenArm

Page 66: Dr Frankenfunctor and the Monadster

Create

Dead Broken Arm Dead Healed Arm

Live Healed Arm

No. We can only heal live arms

Healing a broken arm

HealBrokenArm

Page 67: Dr Frankenfunctor and the Monadster

Dead Broken Arm

Live Healed Arm Live Broken Arm

Create

HealBrokenArm

Healing a broken arm

Page 68: Dr Frankenfunctor and the Monadster

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

Page 69: Dr Frankenfunctor and the Monadster

Dead Broken Arm

M<Live Healed Arm> M<Live Broken Arm>

Create

HealBrokenArm

Healing a broken arm

Page 70: Dr Frankenfunctor and the Monadster

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

Page 71: Dr Frankenfunctor and the Monadster

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

Page 72: Dr Frankenfunctor and the Monadster

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

Page 73: Dr Frankenfunctor and the Monadster

Live Healed Arm Live Broken Arm HealBrokenArm

Healing a broken arm

M<Live Healed Arm> M<Live Broken Arm> HealBrokenArmM

map

Page 74: Dr Frankenfunctor and the Monadster

"map" is generic for M-things

Normal World

a b

Page 75: Dr Frankenfunctor and the Monadster

"map" is generic for M-things

Normal World

a b

map

Page 76: Dr Frankenfunctor and the Monadster

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

Page 77: Dr Frankenfunctor and the Monadster

Show me the code

Broken Arm and "map"

Page 78: Dr Frankenfunctor and the Monadster

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

Page 79: Dr Frankenfunctor and the Monadster

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

Page 80: Dr Frankenfunctor and the Monadster

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

Page 81: Dr Frankenfunctor and the Monadster

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

Page 82: Dr Frankenfunctor and the Monadster

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

Page 83: Dr Frankenfunctor and the Monadster

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.

Page 84: Dr Frankenfunctor and the Monadster

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

Page 85: Dr Frankenfunctor and the Monadster

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

Page 86: Dr Frankenfunctor and the Monadster

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}

Page 87: Dr Frankenfunctor and the Monadster

The importance of map

Page 88: Dr Frankenfunctor and the Monadster

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

Page 89: Dr Frankenfunctor and the Monadster

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

Page 90: Dr Frankenfunctor and the Monadster

The importance of map

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Page 91: Dr Frankenfunctor and the Monadster

The importance of map

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Page 92: Dr Frankenfunctor and the Monadster

The importance of map

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Page 93: Dr Frankenfunctor and the Monadster

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

Page 94: Dr Frankenfunctor and the Monadster

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

Page 95: Dr Frankenfunctor and the Monadster

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

Page 96: Dr Frankenfunctor and the Monadster

How not to code with lists

addTwo

World of normal values

World of Lists

Page 97: Dr Frankenfunctor and the Monadster

How to code with lists

addTwo_L

World of normal values

World of Lists

Page 98: Dr Frankenfunctor and the Monadster

How to code with lists

T -> U

List<T> -> List<U>

List.map

World of normal values

World of Lists

Linq.Select

Page 99: Dr Frankenfunctor and the Monadster

How to code with lists

addTwo

addTwo_L [1;2] |>

1 |> // 3

// [3;4]

World of normal values

World of Lists

Page 100: Dr Frankenfunctor and the Monadster

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

Page 101: Dr Frankenfunctor and the Monadster

Technique 4:

Combining two live parts

Page 102: Dr Frankenfunctor and the Monadster

Combining two parts

Dead Lower Arm

Dead Upper Arm

Live Whole Arm

What we've got What we want

Page 103: Dr Frankenfunctor and the Monadster

Combining two parts

Live Arm Live Lower Arm ArmSurgery

Live Whole Arm

Live Upper Arm

We have this function!

Page 104: Dr Frankenfunctor and the Monadster

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

Page 105: Dr Frankenfunctor and the Monadster

Live Arm

Combining two parts

Dead Lower Arm Dead Upper Arm Dead Arm

Create

ArmSurgery

Page 106: Dr Frankenfunctor and the Monadster

Live Arm

Combining two parts

Dead Lower Arm Dead Upper Arm Dead Arm

Create

No. Only works on live arms

ArmSurgery

Page 107: Dr Frankenfunctor and the Monadster

Create

ArmSurgery

Combining two parts

Dead Lower Arm Dead Upper Arm

Live Lower Arm Live Upper Arm Live Arm

Create

Page 108: Dr Frankenfunctor and the Monadster

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

Page 109: Dr Frankenfunctor and the Monadster

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

Page 110: Dr Frankenfunctor and the Monadster

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

Page 111: Dr Frankenfunctor and the Monadster

Combining two parts

map2

ArmSurgery Live Lower Arm Live Upper Arm Live Arm

ArmSurgeryM M<LiveLowerArm> M<LiveUpperArm> M<LiveArm>

Page 112: Dr Frankenfunctor and the Monadster

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

Page 113: Dr Frankenfunctor and the Monadster

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

Page 114: Dr Frankenfunctor and the Monadster

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

Page 115: Dr Frankenfunctor and the Monadster

Technique 5:

Combining live and dead parts

Page 116: Dr Frankenfunctor and the Monadster

Combining mismatched parts

Empty Head Dead Brain

What we've got

Live Head

What we want

Page 117: Dr Frankenfunctor and the Monadster

Combining mismatched parts

Live Head Live Brain HeadSurgery Empty Head

Combining function alive not alive

Page 118: Dr Frankenfunctor and the Monadster

Create

HeadSurgery

Combining mismatched parts

Dead Brain Empty Head

Live Brain Empty Head Live Head

Copy

Page 119: Dr Frankenfunctor and the Monadster

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

Page 120: Dr Frankenfunctor and the Monadster

Create

HeadSurgeryM

Combining mismatched parts

Dead Brain Empty Head

M<Live Brain> M<Empty Head> M<Live Head>

Page 121: Dr Frankenfunctor and the Monadster

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

Page 122: Dr Frankenfunctor and the Monadster

Combining mismatched parts

return

Anything

M<Anything>

Page 123: Dr Frankenfunctor and the Monadster

Create

HeadSurgeryM

Combining mismatched parts

Dead Brain

M<Live Brain> M<Empty Head> M<Live Head>

return

Empty Head

Page 124: Dr Frankenfunctor and the Monadster

Create

HeadSurgeryM

Combining mismatched parts

Dead Brain

M<Live Brain> M<Empty Head> M<Live Head>

return

Empty Head

Both are M-things now

Page 125: Dr Frankenfunctor and the Monadster

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

Page 126: Dr Frankenfunctor and the Monadster

"return" for M-things

return

Normal World

a

World of M<_> things

A value in the world of normal things

Page 127: Dr Frankenfunctor and the Monadster

"return" for M-things

return

Normal World

a

World of M<_> things

M<a>

A value in the world of M-things

Page 128: Dr Frankenfunctor and the Monadster

"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

Page 129: Dr Frankenfunctor and the Monadster

Technique 6:

Chaining M-generating functions

Page 130: Dr Frankenfunctor and the Monadster

Chaining functions

Beating Heart Dead Heart

What we want What we've got

Page 131: Dr Frankenfunctor and the Monadster

Chaining functions

Beating Heart Live Heart Dead Heart

Creating a beating heart is a two-step process

Page 132: Dr Frankenfunctor and the Monadster

Chaining functions

Dead Heart M<Live Heart> Live Heart M<Beating Heart>

We have an M-generating function

We have another M-generating function

Page 133: Dr Frankenfunctor and the Monadster

Dead Heart M<Live Heart>

Live Heart M<Beating Heart>

Chaining functions

Output type doesn't match input type

Page 134: Dr Frankenfunctor and the Monadster

Dead Heart M<Live Heart>

Live Heart M<Beating Heart>

Chaining functions

Page 135: Dr Frankenfunctor and the Monadster

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!

Page 136: Dr Frankenfunctor and the Monadster

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

Page 137: Dr Frankenfunctor and the Monadster

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

Page 138: Dr Frankenfunctor and the Monadster

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

Page 139: Dr Frankenfunctor and the Monadster

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

Page 140: Dr Frankenfunctor and the Monadster

Show me the code

Beating Heart and "bind"

Page 141: Dr Frankenfunctor and the Monadster

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

Demo: Chaining

Page 142: Dr Frankenfunctor and the Monadster

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

Demo: Chaining

Page 143: Dr Frankenfunctor and the Monadster

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

Demo: Chaining

Page 144: Dr Frankenfunctor and the Monadster

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!

Page 145: Dr Frankenfunctor and the Monadster

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

Page 146: Dr Frankenfunctor and the Monadster

The importance of bind

Page 147: Dr Frankenfunctor and the Monadster

"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

Page 148: Dr Frankenfunctor and the Monadster

"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

Page 149: Dr Frankenfunctor and the Monadster

The importance of bind

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

"Diagonal" functions

Page 150: Dr Frankenfunctor and the Monadster

The importance of bind

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Bind

"SelectMany“ in C#

Page 151: Dr Frankenfunctor and the Monadster

The importance of bind

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Bind

Page 152: Dr Frankenfunctor and the Monadster

The importance of bind

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

Bind

Page 153: Dr Frankenfunctor and the Monadster

The importance of bind

World of normal values

int string bool

World of Lists

List<int> List<string> List<bool>

“Horizontal" functions

Page 154: Dr Frankenfunctor and the Monadster

Technique 7:

Lifting arbitrary functions

Making "map3", "map4", "map5"

on the fly

Page 155: Dr Frankenfunctor and the Monadster

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

Defining the whole body

Page 156: Dr Frankenfunctor and the Monadster

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?

Page 157: Dr Frankenfunctor and the Monadster

Introducing "apply"

apply World of M<_> things

M<(a->b)>

World of M<_> things

Page 158: Dr Frankenfunctor and the Monadster

Introducing "apply"

apply World of M<_> things

M<(a->b)>

World of M<_> things

M<a> M<b>

Page 159: Dr Frankenfunctor and the Monadster

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>

Page 160: Dr Frankenfunctor and the Monadster

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>

Page 161: Dr Frankenfunctor and the Monadster

Using "apply" to make "map3"

apply M<(a->b->c->d)> M<a> M<b->c->d>

Page 162: Dr Frankenfunctor and the Monadster

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>

Page 163: Dr Frankenfunctor and the Monadster

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>

Page 164: Dr Frankenfunctor and the Monadster

Using "apply" to make "map3"

a->b->c->Result a b c Result

Page 165: Dr Frankenfunctor and the Monadster

Using "apply" to make "map3"

M<(a->b->c->d)>

a->b->c->Result a b c Result

return

Page 166: Dr Frankenfunctor and the Monadster

Using "apply" to make "map3"

apply M<(a->b->c->d)> M<a>

a->b->c->Result a b c Result

return create

Page 167: Dr Frankenfunctor and the Monadster

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

Page 168: Dr Frankenfunctor and the Monadster

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

Page 169: Dr Frankenfunctor and the Monadster

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

Page 170: Dr Frankenfunctor and the Monadster

Show me the code

Whole body and "apply"

Page 171: Dr Frankenfunctor and the Monadster

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

Page 172: Dr Frankenfunctor and the Monadster

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

Page 173: Dr Frankenfunctor and the Monadster

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

Page 174: Dr Frankenfunctor and the Monadster

Is your brain hurting now?

Page 175: Dr Frankenfunctor and the Monadster

Do we still have two problems?

Page 176: Dr Frankenfunctor and the Monadster

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"

Page 177: Dr Frankenfunctor and the Monadster

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"

Page 178: Dr Frankenfunctor and the Monadster

A Functional Toolbox

Page 179: Dr Frankenfunctor and the Monadster

map

return bind

map2

apply

Page 180: Dr Frankenfunctor and the Monadster

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

Page 181: Dr Frankenfunctor and the Monadster

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

Page 182: Dr Frankenfunctor and the Monadster

The State monad

The state is threaded through the

code "invisibly"

Page 183: Dr Frankenfunctor and the Monadster

let beatingHeartM = DeadHeart "Anne" |> makeLiveHeart |> makeBeatingHeartM // TWO units of force used up

State monad

Where is the "vital force" tracking variable?

Page 184: Dr Frankenfunctor and the Monadster

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

Page 185: Dr Frankenfunctor and the Monadster

Using Dr Frankenfunctor's

techniques in the real world

Is this too academic? Too abstract to be useful?

Page 186: Dr Frankenfunctor and the Monadster

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

Page 187: Dr Frankenfunctor and the Monadster

Validate

Generates a possible error

Page 188: Dr Frankenfunctor and the Monadster

Validate Canonicalize

Generates a possible error Always succeeds

Page 189: Dr Frankenfunctor and the Monadster

Validate Canonicalize DbFetch

Generates a possible error Generates a possible error Always succeeds

Page 190: Dr Frankenfunctor and the Monadster

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?

Page 191: Dr Frankenfunctor and the Monadster

World of normal things

"lift" from this world

World of two-track things

to this world

Page 192: Dr Frankenfunctor and the Monadster

World of normal things

World of two-track things

bind map apply

Page 193: Dr Frankenfunctor and the Monadster

map

Converting everything to two-track

Page 194: Dr Frankenfunctor and the Monadster

bind

map

Converting everything to two-track

Page 195: Dr Frankenfunctor and the Monadster

bind

map

tee map

Converting everything to two-track

Page 196: Dr Frankenfunctor and the Monadster

Validate Canonicalize DbFetch DbUpdate

Now we *can* glue these together easily!

map bind tee, then map

Page 197: Dr Frankenfunctor and the Monadster

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!

Page 198: Dr Frankenfunctor and the Monadster

Thanks!

@ScottWlaschin

fsharpforfunandprofit.com/monadster

Contact me

Slides and video here

Let us know if you need help with F#