Representations count

Cd9b247e4507fed75312e9a42070125d?s=47 Tom Stuart
February 07, 2019

Representations count

In your grandparents’ attic you discover a mysterious old computer. You boot it up and discover it runs Ruby, but doesn’t support negative numbers! How can you implement negative numbers in an elegant way? We’ll explore two solutions and discover how important it is to pick the right representation.

Cd9b247e4507fed75312e9a42070125d?s=128

Tom Stuart

February 07, 2019
Tweet

Transcript

  1. REPRESENTATIONS @tomstuart, RubyConf AU 2019 https://codon.com/representations-count COUNT

  2. This is a fairy tale.

  3. None
  4. $ irb >> 5 * 2 => 10 >> 5

    - 2 => 3 >> 2 - 5 ImpossibleNumberError (0 is the smallest number)
  5. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  6. $ irb >> require 'signed_number' => true >> def pos(number)

    SignedNumber.positive(number) end => :pos >> def neg(number) SignedNumber.negative(number) end => :neg
  7. >> pos(2) + pos(5) == pos(7) => true >> pos(2)

    - pos(5) == neg(3) => true >> (pos(9) - pos(3)) * (neg(4) + neg(3)) == neg(42) => true >> pos(2) * neg(3) < pos(4) + neg(9) => true
  8. Take a sec to think.

  9. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  10. 3 -

  11. 3 sign (+ or –) size -

  12. class SignedNumber def self.positive(number) end def self.negative(number) end # …

    end protected attr_reader :sign, :size def initialize(sign, size) @sign, @size = sign, size end new(:negative, number) new(:positive, number)
  13. ==

  14. sign == other.sign && size == other.size class SignedNumber #

    … def ==(other) end # … end
  15. >> pos(1) == pos(1) => true >> neg(2) == neg(2)

    => true >> pos(1) == pos(2) => false >> pos(1) == neg(1) => false ✔ ✔ ✔ ✔
  16. +

  17. SignedNumber.new(sign, size + other.size) end # … end class SignedNumber

    # … def +(other)
  18. >> pos(3) + pos(1) == pos(4) => true >> neg(3)

    + neg(1) == neg(4) => true >> pos(3) + neg(1) == pos(2) => false >> pos(3) + neg(1) => #<SignedNumber @sign=:positive, @size=4> ✔ ✔ ✘
  19. else SignedNumber.new(sign, size - other.size) class SignedNumber # … def

    +(other) if sign == other.sign class SignedNumber # … def +(other) SignedNumber.new(sign, size + other.size) end # … end end
  20. >> pos(3) + neg(1) == pos(2) => true >> neg(3)

    + pos(1) == neg(2) => true >> pos(1) + neg(3) == neg(2) ImpossibleNumberError (0 is the smallest number) ✔ ✔
  21. else SignedNumber.new( sign == :positive ? :negative : :positive, other.size

    - size ) end end end # … end class SignedNumber # … def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else SignedNumber.new(sign, size - other.size) if size >= other.size
  22. >> pos(1) + neg(3) == neg(2) => true >> neg(1)

    + pos(3) == pos(2) => true >> pos(3) + neg(3) == neg(3) + pos(3) => false >> pos(3) + neg(3) => #<SignedNumber @sign=:positive, @size=0> >> neg(3) + pos(3) => #<SignedNumber @sign=:negative, @size=0> >> pos(0) == neg(0) => false ✔ ✘ ✘ ✔
  23. class SignedNumber # … def ==(other) end # … end

    ( || size.zero?) && size == other.size sign == other.sign
  24. >> pos(3) + neg(3) == neg(3) + pos(3) => true

  25. None
  26. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  27. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other)
  28. )a end end end def -(other) if sign == other.sign

    if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size)
  29. other.size - size )b end else SignedNumber.new(sign, size + other.size)

    end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size
  30. SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end

    end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  31. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  32. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  33. This was too hard.

  34. Can we do better?

  35. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  36. 3 -

  37. $3 -

  38. Your balance: -$3 Your balance: $7 deposit $10

  39. Your balance: -$3 Your balance: $97 deposit $100

  40. Your balance: $3 Your balance: $13 deposit $10

  41. +2 +2 +3

  42. -2 -2 -3

  43. This has useful properties.

  44. Regularity.

  45. -2 :negative, 2 :negative, 1 :negative, 0 :positive, 0 :positive,

    1 :positive, 2 -1 0 +1 +2
  46. -2 -1 0 +1 +2 … … … … …

  47. Visual intuition.

  48. +2

  49. None
  50. None
  51. -2

  52. +3 +2 +

  53. None
  54. +5

  55. Compositionality.

  56. +3 +2 -

  57. None
  58. None
  59. +1

  60. protected attr_reader :left, :right def initialize(left, right) @left, @right =

    left, right end end class SignedNumber def self.positive(number) end def self.negative(number) end # … new(number, 0) new(0, number)
  61. ==

  62. +2 +3 = ?

  63. None
  64. None
  65. +3 +2 = ?

  66. None
  67. None
  68. = ? +2 +2

  69. None
  70. None
  71. +2 = +2 +2 = +3 +3 = +2

  72. +2 = +2 +2 = +3 +3 = +2 left

    right right left + +
  73. left + other.right == other.left + right end # …

    end class SignedNumber # … def ==(other)
  74. <

  75. +2 < +2 +2 < +3 +3 < +2

  76. +2 < +2 +2 < +3 +3 < +2 left

    right right left + +
  77. other.left + right < left + other.right end # …

    end class SignedNumber # … def <(other)
  78. +

  79. +3 +2 +

  80. None
  81. None
  82. left right left right + +

  83. SignedNumber.new(left + other.left, right + other.right) end # … end

    class SignedNumber # … def +(other)
  84. -

  85. +2 +3 -

  86. None
  87. None
  88. left right right left + +

  89. SignedNumber.new(left + other.right, other.left + right) end # … end

    class SignedNumber # … def -(other)
  90. *

  91. × +4 +5

  92. None
  93. None
  94. None
  95. None
  96. None
  97. =

  98. - =

  99. - = +

  100. - + = -

  101. - + = - 48 16 6 18 20

  102. = 54 34 20 -

  103. - =

  104. × = +4 +5 +20

  105. SignedNumber.new( left * other.right + other.left * right, left *

    other.left + right * other.right ) end # … end class SignedNumber # … def *(other)
  106. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
  107. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
  108. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end class SignedNumber # … def ==(other) (sign == other.sign || size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  109. None
  110. This is a fairy tale.

  111. Regularity. Visual intuition. Compositionality.

  112. Representations count, so choose wisely.

  113. None
  114. tomstuart/negative-numbers

  115. @tomstuart https://codon.com/representations-count THANKS!