Top Banner
The disaster of mutable state
64

The disaster of mutable state

Nov 22, 2014

Download

Software

kenbot

Explores the disastrous problems that mutable state and side effects entail, and discusses how to overcome them.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: The disaster of mutable state

The disaster of mutable state

Page 2: The disaster of mutable state

What’s the big deal

1. Referential transparency is super important2. Effects and mutability do immense damage3. How to write a pure core with a thin effectful

crust

Page 3: The disaster of mutable state

Referential transparency

• For the given inputs, we will always get the same outputs

• Like a mathematical function• You can substitute an expression with its

results

Page 4: The disaster of mutable state

Benefits

• Easier to reason about• Easier to test• Easier to compose & recombine• More modular• Safe caching• Share data with impunity• Thread-safe

Page 5: The disaster of mutable state

Drawbacks?

• Requires some discipline to maintain• If you break RT in one place, it ruins

everything• Updating data requires copying data

structures, which might be expensive (or not)

Page 6: The disaster of mutable state

What’s off the menu?

• Network/DB/File system I/O• Mutating state• Reading/observing state that can be mutated• Creating a mutable object• Reference equality• Time.now()• random()• Exceptions

Page 7: The disaster of mutable state

Equational reasoning

• Our starting point is no more complex than the final result, and can be replaced without changing the meaning

• Order doesn’t matter

2 + (25 * 3) – 7= 2 + 75 – 7= 77 – 7= 70

Page 8: The disaster of mutable state

Pure exampledef add(a,b) a + bend

def mult(a,b) a * bend

# Interchangeable# Evaluation order doesn’t matteradd(4, mult(3,2))add(4, 6)10

Page 9: The disaster of mutable state

Impure examplemutavar = 0

def mult(num) mutavar *= numend

def add(num) mutavar += numend

# Cannot change order# Cannot substitute actions for resultsadd(5)mult(2)add(-2)

Page 10: The disaster of mutable state

Composition pure style!

• Give input, check output• output = foo(bar(baz(input)))

Page 11: The disaster of mutable state

Composition pure style!

• Give input, check output• output = foo(bar(baz(input)))

Page 12: The disaster of mutable state

Composition pure style!

• Give input, check output• output = foo(bar(baz(input)))

Page 13: The disaster of mutable state

Composition pure style!

• Put something inside another thing• output = foo(bar(baz(input)))

Page 14: The disaster of mutable state

Testing pure style!

• Give input, check output• output == foo(bar(baz(input)))

Give it one of these …and we get this?

Page 15: The disaster of mutable state

Composition mutable style

Page 16: The disaster of mutable state

Composition mutable style

Page 17: The disaster of mutable state

Composition mutable style

Page 18: The disaster of mutable state

Composition mutable style

Page 19: The disaster of mutable state

Composition mutable style

Page 20: The disaster of mutable state

Composition mutable style

Page 21: The disaster of mutable state

Composition mutable style

Page 22: The disaster of mutable state

Composition mutable style

Page 23: The disaster of mutable state

Composition mutable style

Page 24: The disaster of mutable state

Composition mutable style

Page 25: The disaster of mutable state

Composition mutable style

Page 26: The disaster of mutable state

Composition mutable style

Page 27: The disaster of mutable state

Composition mutable style

Page 28: The disaster of mutable state

Composition mutable style

Page 29: The disaster of mutable state

Composition mutable style

• We need to fit every level of detail in our head! We can’t ignore anything.

• Any level could fiddle with something that changes the whole• Non-modular• Not really “composition” in a true sense

Page 30: The disaster of mutable state

Testing mutable style! AKA “web of lies”

MOCK – HANDLE WITH CARE

MOCK – HANDLE WITH CARE

If I say “Gerbil”, then you say “Party time!”

And then you call my thing with a baked potato!

The 3rd time I knock, then you put on a Darth Vader mask and breath heavily

Let’s totally alter the fabric of space-time.

Page 31: The disaster of mutable state

Modularity (pure)

What does this do?Elitist academic Ivory Tower genius

Page 32: The disaster of mutable state

Modularity (pure)

What does this do?Elitist academic Ivory Tower genius

Page 33: The disaster of mutable state

Modularity (with side-effects)

What does this do? Pragmatic real world coder who gets shit done

Page 34: The disaster of mutable state

Modularity (with side-effects)

What does this do? Pragmatic real world coder who gets shit done

Page 35: The disaster of mutable state

Modularity (with side-effects)

What does this do? Pragmatic real world coder who gets shit done

Page 36: The disaster of mutable state

Modularity (with side-effects)

What does this do? Pragmatic real world coder who gets shit done

Page 37: The disaster of mutable state

Modularity (with side-effects)

What does this do? Pragmatic real world coder who gets shit done

Expected order1) This2) That3) Depends4) ???5) Touches here6) Widdles the widget7) Diddles the

doobilacky

Gets a bit hairy here

Best we don’t modify this

Page 38: The disaster of mutable state

“I’m not smart enough to use mutable state!”

- Functional programmers

Page 39: The disaster of mutable state

State, time & identity

“But the real world is mutable!”

Page 40: The disaster of mutable state

State, time & identity

samuel_l_jackson.hairstylesamuel_l_jackson.occupation

Page 41: The disaster of mutable state

State, time & identity

samuel_l_jackson.hairstylesamuel_l_jackson.occupation

=> “bald”=> “actor”

Page 42: The disaster of mutable state

State, time & identity

samuel_l_jackson.hairstylesamuel_l_jackson.occupation

=> “bald”=> “actor”... in 2014

Page 43: The disaster of mutable state

State, time & identity

samuel_l_jackson.hairstylesamuel_l_jackson.occupation

=> “afro”=> “student”

1960s:

Page 44: The disaster of mutable state

State, time & identity

“Samuel L Jackson”

1960 1970 1980 1990 2000 2010

H: “bald”O: “actor”

H: “afro”O: “student”

Identity

Page 45: The disaster of mutable state

State, time & identity

• Mutable state implies identity• This totally changes the nature of the object• Identities refer to different states over time• Each state is eternal and immutable

Page 46: The disaster of mutable state

Arguably OK identity

class ToyRobot attr_accessor :facing, :posend

Page 47: The disaster of mutable state

Arguably OK identity

• ToyRobot will hold many facing/position states over time

• Robot’s identity is at least a meaningful concept

Page 48: The disaster of mutable state

Totally wrong and broken identity

class Position attr_accessor :x, :yend

Page 49: The disaster of mutable state

Totally wrong and broken identity

• A position can change under your feet!• What can it possibly mean??• What use is an identity that strings together

different Position states over time?

Page 50: The disaster of mutable state

Consider…

class Integer attr_accessor :value def plus(n); value += n.value; endend

three = Integer.new 3three.value = 4three.plus(three) 8

Page 51: The disaster of mutable state

Better: separate the identity

class ToyRobot attr_reader :facing, :posend

current_robots = { :r2d2 => ToyRobot.new ... :c3p0 => ... :dexter => ... :rdolivaw => ...}

Page 52: The disaster of mutable state

Emphasis on transitions

• No: “Take this robot and change it”• Yes: “Specify the transformation from one

robot to another”

Page 53: The disaster of mutable state

But this map is still mutable!

• At least we have shunted the mutability out a layer!

current_robots = { :r2d2 => ...}

current_robots.each { |id, bot| current_robots[:id] = bot.update()}

Page 54: The disaster of mutable state

One more level: immutable hashes

• https://github.com/hamstergem/hamster

current_robots = Hamster.hash( :r2d2 => ToyRobot.new ... ...)

current_robots.fold(Hamster.hash) { |next_robots, id, bot| next_robots.put(id, bot.update)}

Page 55: The disaster of mutable state

One more level: immutable world

class World attr_reader :current_robots

def update # return updated world end end

def whole_program(current_world) whole_program(current_world.update)end

Page 56: The disaster of mutable state

Separating decisions from actions

• Often side-effecting code mixes decisions with actions

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) send_email(email, content) else log(“[info] Couldn’t send email”) endend

Page 57: The disaster of mutable state

Separating decisions from actions

1) Validity checking (pure)

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) send_email(email, content) else log(“[info] Couldn’t send email”) endend

Page 58: The disaster of mutable state

Separating decisions from actions

2) Generate email content (pure)

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) send_email(email, content) else log(“[info] Couldn’t send email”) endend

Page 59: The disaster of mutable state

Separating decisions from actions

3) Decide to send (pure), and act on it (I/O)

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) send_email(email, content) else log(“[info] Couldn’t send email”) endend

Page 60: The disaster of mutable state

Separating decisions from actions

4) Decide to log the failure (pure), and do it (I/O)

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) send_email(email, content) else log(“[info] Couldn’t send email”) endend

Page 61: The disaster of mutable state

Explicit decision

class Decision; end

class SendEmail < Decision attr_reader :email, :contentend

class Log < Decision attr_reader :messageend

class DoNothing < Decision; end

Page 62: The disaster of mutable state

Pure version

def send_welcome_email email, pwd if valid(email, pwd) content = create_content(email) SendEmail.new(email, content) else Log.new( “[info] Couldn’t send email”) endend

Page 63: The disaster of mutable state

Party at the end of the worlddef run_pure # Return decisions [ ... ]end

def interpret_decision d case d when Log puts d.message when SendEmail actually_send d.email, d.content when DoNothing endend

Page 64: The disaster of mutable state

Big problems; huge wins

1. Mutable state causes immense harm to our software

2. Referential transparency and purity bring corresponding benefits

3. It’s not hard to start reaping the benefits