the disaster of mutable state

64
The disaster of mutable state

Upload: kenbot

Post on 22-Nov-2014

425 views

Category:

Software


0 download

DESCRIPTION

Explores the disastrous problems that mutable state and side effects entail, and discusses how to overcome them.

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