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. Immortal Ruby

    View Slide

  2. Marcos Matos

    View Slide

  3. marcosccm

    View Slide

  4. ThoughtWorks

    View Slide

  5. Immutability

    View Slide

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

    View Slide

  7. “Cool! The problem
    magically fixed itself”
    !
    Do you really believe in that?

    View Slide

  8. It’s a BUG!

    View Slide

  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

    View Slide

  10. The path of the bug is full
    of complexity and
    misunderstanding
    !
    or anger and small paychecks

    View Slide

  11. Many smart folks believe that
    our dependency on mutable
    state is a huge cause of
    complexity
    !
    See all of Rich Hickey’s presentations…

    View Slide

  12. mutable state led us
    to the
    TAR PIT

    View Slide

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

    View Slide

  14. I must not mutate state.
    State is the code-killer.
    State is the little-death that
    brings total complication.
    !
    The Litany against State

    View Slide

  15. but that’s how I
    learned how to
    program!

    View Slide

  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

    View Slide

  17. small caveat

    View Slide

  18. we need some state!

    View Slide

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

    View Slide

  20. Data vs State

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. Solution #1
    !
    write everything in
    Haskell

    View Slide

  25. no, I want to keep
    using Ruby

    View Slide

  26. Solution #2
    !
    just
    mutate less stuff

    View Slide

  27. nothing in Ruby
    prevents you from
    writing immutable
    code

    View Slide

  28. nothing helps
    either…

    View Slide

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

    View Slide

  30. Values vs Identity

    View Slide

  31. Values
    !
    Atomic concepts that
    cannot change

    View Slide

  32. Values
    !
    42, phone numbers, dates

    View Slide

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

    View Slide

  34. Identity
    !
    you, me, your bank
    account

    View Slide

  35. How Ruby handles
    this?

    View Slide

  36. > 42
    => 42
    !
    => 42.next
    > 43
    !
    > n = 42
    > n = n + 1
    => 43
    !
    > 42 = 42 + 1
    => WhatTheHellAreYouDoingError
    You can’t change natural numbers

    View Slide

  37. > dish1 = “potato”
    > dish2 = dish1
    > dish1[0] = “d”
    !
    > puts dish1
    => “dotato”
    !
    > puts dish2
    => “dotato”
    You can, however, redefine the Scottish cuisine

    View Slide

  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

    View Slide

  39. how we avoid
    mutating our values?

    View Slide

  40. > dish1 = “potato”
    > dish2 = dish1
    !
    > dish1 = dish1.sub(’b’,’d’)
    !
    > puts dish1
    => “dotato”
    !
    > puts dish2
    => “potato”
    avoid the !

    View Slide

  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

    View Slide

  42. > glasgow = “rainy”
    > glasgow.freeze
    !
    > glasgow.sub!(“rainy”, “sunny”)
    => Can’t modify frozen String (RunTimeError)
    What killed the dinosaurs? The Ice Age!
    Mr Freeze

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  47. what about more
    complex objects?

    View Slide

  48. Value Objects
    !
    Objects that are defined
    by the total of their values

    View Slide

  49. Don’t give a value object any
    identity and avoid the design
    complexities necessary to
    maintain entities.
    Eric Evans
    on value objects

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  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

    View Slide

  54. Hurray!
    !
    We have Immutable
    Addresses!

    View Slide

  55. what about our
    core domain
    objects?

    View Slide

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

    View Slide

  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

    View Slide

  58. Objects
    a snapshot of state and
    methods that exposes
    possibilities on top of that state

    View Slide

  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

    View Slide

  60. we can reduce state
    mutation where it
    really matters!

    View Slide

  61. what about
    Active Record?

    View Slide

  62. class Address < ActiveRecord::Base
    attr_readonly :street
    attr_readonly :complement
    attr_readonly :city
    end
    good guy active record

    View Slide

  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?

    View Slide

  64. what about
    performance?

    View Slide

  65. Immutability
    everywhere

    View Slide

  66. Immutable Databases
    Datomic
    !
    Immutable Frontend
    ClojureScript

    View Slide

  67. Recap

    View Slide

  68. Mutable state
    brings
    complexity

    View Slide

  69. We can have
    Immutability in
    Ruby

    View Slide

  70. go beyond
    Value Objects

    View Slide

  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

    View Slide

  72. Obrigado!

    View Slide

  73. Discussion Time!

    View Slide