Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Immortal Ruby

Immortal Ruby

Talk about Immutability and Ruby presented on RubyNation 2014

Transcript

  1. Immortal Ruby

  2. Marcos Matos

  3. marcosccm

  4. ThoughtWorks

  5. Immutability

  6. Have you tried rebooting the server? ! the complete guide

    to fixing software
  7. “Cool! The problem magically fixed itself” ! Do you really

    believe in that?
  8. It’s a BUG!

  9. I would have written simpler code, but I did not

    have the time. ! Blaise Pascal (or maybe Mark Twain, or Newton) on writing code
  10. The path of the bug is full of complexity and

    misunderstanding ! or anger and small paychecks
  11. Many smart folks believe that our dependency on mutable state

    is a huge cause of complexity ! See all of Rich Hickey’s presentations…
  12. mutable state led us to the TAR PIT

  13. state makes ! Reasoning hard Testing hard Concurrency hard Life

    hard
  14. I must not mutate state. State is the code-killer. State

    is the little-death that brings total complication. ! The Litany against State
  15. but that’s how I learned how to program!

  16. atual <- 1! ant <- 0! repita! num <- atual

    + ant! escreva ("num")! ant <- atual! atual <- num! cont <- cont + 1! ate (cont = n-2) The (not really) amazing Portugol
  17. small caveat

  18. we need some state!

  19. we call it user input ! users are sadly not

    referentially transparent
  20. Data vs State

  21. Data ! Things your application needs in order to work

  22. State ! all the mutations we impose on our data

    without necessity
  23. so how do one atone for his sinful stateful ways?

  24. Solution #1 ! write everything in Haskell

  25. no, I want to keep using Ruby

  26. Solution #2 ! just mutate less stuff

  27. nothing in Ruby prevents you from writing immutable code

  28. nothing helps either…

  29. Why is so hard to write immutable OO code?

  30. Values vs Identity

  31. Values ! Atomic concepts that cannot change

  32. Values ! 42, phone numbers, dates

  33. Identity ! A logical entity composed by values that change

    over time
  34. Identity ! you, me, your bank account

  35. How Ruby handles this?

  36. > 42 => 42 ! => 42.next > 43 !

    > n = 42 > n = n + 1 => 43 ! > 42 = 42 + 1 => WhatTheHellAreYouDoingError You can’t change natural numbers
  37. > dish1 = “potato” > dish2 = dish1 > dish1[0]

    = “d” ! > puts dish1 => “dotato” ! > puts dish2 => “dotato” You can, however, redefine the Scottish cuisine
  38. > col1 = [1,2,3] > col2 = col1 > col1

    << 4 ! > puts col1 => [1,2,3,4] ! > puts col2 => [1,2,3,4] Arrays suffer the same fate
  39. how we avoid mutating our values?

  40. > dish1 = “potato” > dish2 = dish1 ! >

    dish1 = dish1.sub(’b’,’d’) ! > puts dish1 => “dotato” ! > puts dish2 => “potato” avoid the !
  41. > col1 = [1,2,3] > col2 = col1.dup > col1

    << 4 ! > puts col1 => [1,2,3,4] ! > puts col2 => [1,2,3] the attack of the clones
  42. > glasgow = “rainy” > glasgow.freeze ! > glasgow.sub!(“rainy”, “sunny”)

    => Can’t modify frozen String (RunTimeError) What killed the dinosaurs? The Ice Age! Mr Freeze
  43. > iceberg = [”big”,”chunk”,”ice”] > iceberg.freeze ! > iceberg.shift(“very”) =>

    RuntimeError: can't modify frozen Array ! > iceberg[0].sub!(“big”, “small”) => [”small”,”chunk”,”ice”] more like Array#chill_out
  44. > require ‘ice_nine' ! > iceberg = [”big”,”chunk”,”ice”] > IceNine.deep_freeze(iceberg)

    ! > iceberg.shift(“very”) => RuntimeError: can't modify frozen Array ! > iceberg[0].sub!(“big”, “small”) => RuntimeError: can't modify frozen String github.com/dkubb/ice_nine
  45. > require ‘hamster' ! > addr = Hamster.hash(street: ‘Queen St’,

    number: ‘152’) => { :street => ‘Queen St’, :number => ‘152’ } ! > with_city = addr.put(:city, ‘Glasgow’) => { :street => ‘..’, :number => ‘..’, city => ‘Glasgow’ } ! > addr => { :street => ‘Queen St’, :number => ‘152’ } https://github.com/hamstergem/hamster
  46. > require ‘hamster' ! > without_city = addr.delete(:city) => {

    :street => ‘Queen St’, :number => ‘152’ } ! > with_city => { :street => ‘..’, :number => ‘..’, city => ‘Glasgow’ } https://github.com/hamstergem/hamster
  47. what about more complex objects?

  48. Value Objects ! Objects that are defined by the total

    of their values
  49. Don’t give a value object any identity and avoid the

    design complexities necessary to maintain entities. Eric Evans on value objects
  50. class Address attr_reader :street, :complement, :city ! def initialize(street, complement,

    city) @street = street @complement = complement @city = city end ! def to_s "#{street}, #{complement}, #{city}" end end A boring Address
  51. > address = Address.new(“Queen st”, “a secret”, “Glasgow”) > puts

    address => “Queen St, a secret, Glasgow” ! > addess.street = “Argyle St” => bla undefined method street=bla bla ! > address.street.gsub!(/Queen/,”Lolz”) > puts address => “Lolz St, a secret, Glasgow” Lolz Encapsulation
  52. require 'ice_nine' ! class Address attr_reader :street, :complement, :city !

    def initialize(street, complement, city) @street = street @complement = complement @city = city IceNine.deep_freeze!(self) end ! def to_s "#{street}, #{complement}, #{city}" end end Immutability at last!
  53. require 'virtus' ! class Address include Virtus.value_object ! values do

    attribute :street, String attribute :complement, String attribute :city, String end end github.com/solnic/virtus
  54. Hurray! ! We have Immutable Addresses!

  55. what about our core domain objects?

  56. Objects a collection of state and methods that operate on

    top of that state
  57. class BankAccount attr_reader :current ! def initialize @current = 0

    end ! def deposit(amount) @current = current + amount end ! def withdrawal(amount) @current = current - amount end end a robust banking system
  58. Objects a snapshot of state and methods that exposes possibilities

    on top of that state
  59. class BankAccount attr_reader :current ! def initialize(current) @current = current

    end ! def deposit(amount) BankAccount.new(current + amount) end ! def withdrawal(amount) BankAccount.new(current - amount) end end no mutations
  60. we can reduce state mutation where it really matters!

  61. what about Active Record?

  62. class Address < ActiveRecord::Base attr_readonly :street attr_readonly :complement attr_readonly :city

    end good guy active record
  63. > address = Address.create(street: “Queen st”) > Address.first.street => “Queen

    St” ! > addess.update_attributes(street: “Argyle St”) > address.street => “Argyle St” ! > Address.find(1).street => “Queen St” bug or feature?
  64. what about performance?

  65. Immutability everywhere

  66. Immutable Databases Datomic ! Immutable Frontend ClojureScript

  67. Recap

  68. Mutable state brings complexity

  69. We can have Immutability in Ruby

  70. go beyond Value Objects

  71. Wanna learn more? ! Amazing blog post from the author

    of the Hamster lib http://www.harukizaemon.com/blog/2010/03/01/functional-programming-in-object-oriented- languages/ ! Anything Rich Hickey http://thechangelog.com/rich-hickeys-greatest-hits/ ! The Tar Pit paper http://shaffner.us/cs/papers/tarpit.pdf ! Clojure’s take on State http://clojure.org/state
  72. Obrigado!

  73. Discussion Time!