Successfully reported this slideshow.

×

# The disaster of mutable state

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

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

## More Related Content

### The disaster of mutable state

1. 1. The disaster of mutable state
2. 2. What’s the big deal 1. Referential transparency is super important 2. Effects and mutability do immense damage 3. How to write a pure core with a thin effectful crust
3. 3. 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
4. 4. Benefits • Easier to reason about • Easier to test • Easier to compose & recombine • More modular • Safe caching • Share data with impunity • Thread-safe
5. 5. 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)
6. 6. 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
7. 7. 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
8. 8. Pure example def add(a,b) a + b end def mult(a,b) a * b end # Interchangeable # Evaluation order doesn’t matter add(4, mult(3,2)) add(4, 6) 10
9. 9. Impure example mutavar = 0 def mult(num) mutavar *= num end def add(num) mutavar += num end # Cannot change order # Cannot substitute actions for results add(5) mult(2) add(-2)
10. 10. Composition pure style! • Give input, check output • output = foo(bar(baz(input)))
11. 11. Composition pure style! • Give input, check output • output = foo(bar(baz(input)))
12. 12. Composition pure style! • Give input, check output • output = foo(bar(baz(input)))
13. 13. Composition pure style! • Put something inside another thing • output = foo(bar(baz(input)))
14. 14. Testing pure style! • Give input, check output • output == foo(bar(baz(input))) Give it one of these …and we get this?
15. 15. Composition mutable style
16. 16. Composition mutable style
17. 17. Composition mutable style
18. 18. Composition mutable style
19. 19. Composition mutable style
20. 20. Composition mutable style
21. 21. Composition mutable style
22. 22. Composition mutable style
23. 23. Composition mutable style
24. 24. Composition mutable style
25. 25. Composition mutable style
26. 26. Composition mutable style
27. 27. Composition mutable style
28. 28. Composition mutable style
29. 29. 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
30. 30. 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.
31. 31. Modularity (pure) What does this do? Elitist academic Ivory Tower genius
32. 32. Modularity (pure) What does this do? Elitist academic Ivory Tower genius
33. 33. Modularity (with side-effects) What does this do? Pragmatic real world coder who gets shit done
34. 34. Modularity (with side-effects) What does this do? Pragmatic real world coder who gets shit done
35. 35. Modularity (with side-effects) What does this do? Pragmatic real world coder who gets shit done
36. 36. Modularity (with side-effects) What does this do? Pragmatic real world coder who gets shit done
37. 37. Modularity (with side-effects) What does this do? Pragmatic real world coder who gets shit done Expected order 1) This 2) That 3) Depends 4) ??? 5) Touches here 6) Widdles the widget 7) Diddles the doobilacky Gets a bit hairy here Best we don’t modify this
38. 38. “I’m not smart enough to use mutable state!” - Functional programmers
39. 39. State, time & identity “But the real world is mutable!”
40. 40. State, time & identity samuel_l_jackson.hairstyle samuel_l_jackson.occupation
41. 41. State, time & identity samuel_l_jackson.hairstyle samuel_l_jackson.occupation => “bald” => “actor”
42. 42. State, time & identity samuel_l_jackson.hairstyle samuel_l_jackson.occupation => “bald” => “actor” ... in 2014
43. 43. State, time & identity samuel_l_jackson.hairstyle samuel_l_jackson.occupatio n => “afro” => “student” 1960s:
44. 44. State, time & identity “Samuel L Jackson” 1960 1970 1980 1990 2000 2010 H: “bald” O: “actor” H: “afro” O: “student” Identity
45. 45. 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
46. 46. Arguably OK identity class ToyRobot attr_accessor :facing, :pos end
47. 47. Arguably OK identity • ToyRobot will hold many facing/position states over time • Robot’s identity is at least a meaningful concept
48. 48. Totally wrong and broken identity class Position attr_accessor :x, :y end
49. 49. 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?
50. 50. Consider… class Integer attr_accessor :value def plus(n); value += n.value; end end three = Integer.new 3 three.value = 4 three.plus(three) 8
51. 51. Better: separate the identity class ToyRobot attr_reader :facing, :pos end current_robots = { :r2d2 => ToyRobot.new ... :c3p0 => ... :dexter => ... :rdolivaw => ... }
52. 52. Emphasis on transitions • No: “Take this robot and change it” • Yes: “Specify the transformation from one robot to another”
53. 53. 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() }
54. 54. 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) }
55. 55. 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
56. 56. 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”) end end
57. 57. 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”) end end
58. 58. 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”) end end
59. 59. 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”) end end
60. 60. 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”) end end
61. 61. Explicit decision class Decision; end class SendEmail < Decision attr_reader :email, :content end class Log < Decision attr_reader :message end class DoNothing < Decision; end
62. 62. 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”) end end
63. 63. Party at the end of the world def 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 end end
64. 64. 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