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

More Decks by Marcos Castilho da Costa Matos

Other Decks in Programming

Transcript

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

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

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

    is a huge cause of complexity ! See all of Rich Hickey’s presentations…
  4. I must not mutate state. State is the code-killer. State

    is the little-death that brings total complication. ! The Litany against State
  5. 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
  6. > 42 => 42 ! => 42.next > 43 !

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

    = “d” ! > puts dish1 => “dotato” ! > puts dish2 => “dotato” You can, however, redefine the Scottish cuisine
  8. > 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
  9. > dish1 = “potato” > dish2 = dish1 ! >

    dish1 = dish1.sub(’b’,’d’) ! > puts dish1 => “dotato” ! > puts dish2 => “potato” avoid the !
  10. > 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
  11. > glasgow = “rainy” > glasgow.freeze ! > glasgow.sub!(“rainy”, “sunny”)

    => Can’t modify frozen String (RunTimeError) What killed the dinosaurs? The Ice Age! Mr Freeze
  12. > 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
  13. > 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
  14. > 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
  15. > require ‘hamster' ! > without_city = addr.delete(:city) => {

    :street => ‘Queen St’, :number => ‘152’ } ! > with_city => { :street => ‘..’, :number => ‘..’, city => ‘Glasgow’ } https://github.com/hamstergem/hamster
  16. Don’t give a value object any identity and avoid the

    design complexities necessary to maintain entities. Eric Evans on value objects
  17. 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
  18. > 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
  19. 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!
  20. 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
  21. 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
  22. 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
  23. > 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?
  24. 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