Do Mix Your Drinks

Do Mix Your Drinks

A talk about how abstract algebra is useful for Ruby programmers, both when writing everyday programs and when going to the pub afterwards. As an example I show how to use my VectorSpace library to compare and manipulate structured data for free.

Given at the July 2009 LRUG meeting (http://lrug.org/meetings/2009/06/17/july-2009-meeting/). There's a video of this talk at http://skillsmatter.com/podcast/ajax-ria/do-mix-your-drinks.

Cd9b247e4507fed75312e9a42070125d?s=128

Tom Stuart

July 08, 2009
Tweet

Transcript

  1. Do Mix Your Drinks @tomstuart LRUG, 2009-07-08

  2. I used to try to be a mathematician but now

    I try to be a programmer
  3. Theory: Use ideas from abstract algebra in your Ruby programs,

    because mathematicians are programmers too
  4. Practice: Use the VectorSpace library to model multidimensional values and

    get their operations for free
  5. Theory:

  6. WIKIPEDIA Abstract algebra From Wikipedia, the free encyclopedia Abstract algebra

    is the subject area of mathematics that studies algebraic structures, such as groups, rings, fields, modules, vector spaces, and algebras.
  7. WIKIPEDIA Algebraic structure From Wikipedia, the free encyclopedia An algebraic

    structure consists of one or more sets closed under one or more operations, satisfying some axioms.
  8. “sets closed under one or more operations”

  9. “sets closed under one or more operations”

  10. “sets closed under one or more operations” “classes with one

    or more methods”
  11. These structures have been well-studied and are well-understood Satisfy the

    axioms and you get some properties
  12. It’s all about abstraction The idea of encapsulating some well-defined

    operations is familiar
  13. A total order is a binary relation (here denoted by

    infix ≤) on some set X. A set paired with a total order is called a totally ordered set. WIKIPEDIA Total order From Wikipedia, the free encyclopedia
  14. If X is totally ordered under ≤, then the following

    statements hold for all a, b and c in X: If a ≤ b and b ≤ a then a = b (antisymmetry); If a ≤ b and b ≤ c then a ≤ c (transitivity); a ≤ b or b ≤ a (totality).
  15. For each (non-strict) total order ≤ there is an associated

    asymmetric (hence irreflexive) relation <, called a strict total order, which can equivalently be defined in two ways: ▪a < b if and only if a ≤ b and a ≠ b ▪a < b if and only if not b ≤ a (i.e., < is the inverse of the complement of ≤) Properties: ▪The relation is transitive: a < b and b < c implies a < c. ▪The relation is trichotomous: exactly one of a < b, b < a and a = b is true. ▪The relation is a strict weak order, where the associated equivalence is equality. Two more associated orders are the complements ≥ and >, completing the quadruple {<, >, ≤, ≥}.
  16. ok boring

  17. None
  18. You define #<=> You get #<, #<=, #==, #>= and

    #> for free
  19. (P.S. define #<=> instead of #<= because it guarantees some

    axioms and makes implementation more efficient)
  20. ok so

  21. This all works because core Ruby implements a mathematical structure

    with known properties
  22. We shouldn’t be afraid to look further afield for other

    structures that might help us out
  23. Practice:

  24. I needed to manipulate multidimensional values I realised the manipulations

    were just operations in a vector space I wrote a tiny library for modelling multidimensional values as vectors
  25. WIKIPEDIA Abstract algebra From Wikipedia, the free encyclopedia Abstract algebra

    is the subject area of mathematics that studies algebraic structures, such as groups, rings, fields, modules, vector spaces, and algebras.
  26. Given a field F (the “scalars”)

  27. A vector space over F is a set equipped with

    two binary operations: vector addition (between two elements of the set), and scalar multiplication (between an element of the set and a scalar)
  28. + =

  29. × = 2

  30. Axiom Signification Associativity of addition u + (v + w)

    = (u + v) + w. Commutativity of addition v + w = w + v. Identity element of addition There exists an element 0 ∈ V, called the zero vector, such that v + 0 = v for all v ∈ V. Inverse elements of addition For all v ∈ V, there exists an element w ∈ V, called the additive inverse of v, such that v + w = 0. The additive inverse is denoted −v. Distributivity of scalar multiplication with respect to vector addition   a(v + w) = av + aw. Distributivity of scalar multiplication with respect to field addition (a + b)v = av + bv. Compatibility of scalar multiplication with field multiplication a(bv) = (ab)v Identity element of scalar multiplication 1v = v, where 1 denotes the multiplicative identity in F.
  31. These axioms entail that subtraction of two vectors and division

    by a (non-zero) scalar can be performed via v − w = v + (−w), v / a = (1 / a) · v.
  32. ok boring

  33. A vector space abstracts the idea of manipulating multiple independent

    values simultaneously
  34. 5? 6? + = × 2 =

  35. + = × 2 =

  36. – = ÷ 2 =

  37. VectorSpace mixin (assuming constructors and getters)

  38. VectorSpace::SimpleVector class (for convenience)

  39. Vector arithmetic (0, +, –, ÷) Scalar arithmetic (×, ÷)

    Partial order (product, lexicographic)
  40. class Round < VectorSpace::SimpleVector has_dimension :hoegaarden has_dimension :franziskaner has_dimension :fruli

    has_dimension :water end >> Round.new => 0 and 0 and 0 and 0 >> Round.new :hoegaarden => 5, :fruli => 3 => 5 and 0 and 3 and 0
  41. class Round < VectorSpace::SimpleVector has_dimension :hoegaarden, :describe => lambda {

    |n| "#{n} Hoegaardens" unless n.zero? } has_dimension :franziskaner, :describe => lambda { |n| "#{n} Franziskaners" unless n.zero? } has_dimension :fruli, :describe => lambda { |n| "#{n} Frülis" unless n.zero? } has_dimension :water, :describe => lambda { |n| "#{n} waters" unless n.zero? } has_zero_description 'no drinks' end
  42. >> Round.new => no drinks >> Round.new :hoegaarden => 5,

    :fruli => 3 => 5 Hoegaardens and 3 Frülis >> Round.new(:hoegaarden => 2, :franziskaner => 3) + Round.new(:water => 2, :fruli => 2, :hoegaarden => 1) => 3 Hoegaardens and 3 Franziskaners and 2 Frülis and 2 waters >> (Round.new(:hoegaarden => 2, :franziskaner => 3) * 2) - Round.new(:hoegaarden => 1) => 3 Hoegaardens and 6 Franziskaners
  43. None
  44. VectorSpace::Family mixin VectorSpace::SimpleIndexedVector class

  45. None
  46. class Cocktail < VectorSpace::SimpleIndexedVector indexed_by :units has_dimension :gin has_dimension :vermouth

    has_dimension :whisky has_dimension :vodka has_dimension :kahlua has_dimension :cream end
  47. class Cocktail < VectorSpace::SimpleIndexedVector indexed_by :units has_dimension :gin, :describe =>

    lambda { |units, n| "#{n}#{units} gin" unless n.zero? } has_dimension :vermouth, :describe => lambda { |units, n| "#{n}#{units} vermouth" unless n.zero? } has_dimension :whisky, :describe => lambda { |units, n| "#{n}#{units} whisky" unless n.zero? } has_dimension :vodka, :describe => lambda { |units, n| "#{n}#{units} vodka" unless n.zero? } has_dimension :kahlua, :describe => lambda { |units, n| "#{n}#{units} Kahlúa" unless n.zero? } has_dimension :cream, :describe => lambda { |units, n| "#{n}#{units} cream" unless n.zero? } end
  48. >> martini = Cocktail.new :units => :cl, :gin => 5.5,

    :vermouth => 1.5 => 5.5cl gin and 1.5cl vermouth >> manhattan = Cocktail.new :units => :cl, :whisky => 5, :vermouth => 2 => 2cl vermouth and 5cl whisky >> white_russian = Cocktail.new :units => :oz, :vodka => 2, :kahlua => 1, :cream => 1.5 => 2oz vodka and 1oz Kahlúa and 1.5oz cream >> martini * 2 => 11cl gin and 3cl vermouth >> martini + manhattan => 5.5cl gin and 3.5cl vermouth and 5cl whisky >> martini + white_russian ArgumentError: can't add 5.5cl gin and 1.5cl vermouth to 2oz vodka and 1oz Kahlúa and 1.5oz cream
  49. This is all pretty trivial but it makes things drastically

    simpler when you need to nest vectors
  50. class Round < VectorSpace::SimpleVector has_dimension :hoegaarden, :describe => lambda {

    |n| "#{n} Hoegaardens" unless n.zero? } has_dimension :franziskaner, :describe => lambda { |n| "#{n} Franziskaners" unless n.zero? } has_dimension :fruli, :describe => lambda { |n| "#{n} Frülis" unless n.zero? } has_dimension :water, :describe => lambda { |n| "#{n} waters" unless n.zero? } has_dimension :cocktail, :describe => lambda { |c| "a cocktail of #{c}" unless c.zero? } has_zero_description 'no drinks' end
  51. >> Round.new(:hoegaarden => 2, :cocktail => martini) + Round.new(:fruli =>

    3, :cocktail => manhattan) => 2 Hoegaardens and 3 Frülis and a cocktail of 5.5cl gin and 3.5cl vermouth and 5cl whisky
  52. Money = family of one- dimensional vector spaces indexed by

    currency
  53. TaxedMoney = family of two-dimensional vector spaces indexed by currency

  54. RegionalPrice = n-dimensional vector space, underlying values are Money

  55. Credits = n-dimensional vector space, comparison “can I afford?”, division

    “how many?”
  56. Payment = two- dimensional vector space, underlying values are TaxedMoney

    and Credits
  57. ok that’s it

  58. If you liked this: • Download VectorSpace from http://github.com/tomstuart •

    Follow @tomstuart on Twitter • Round.new :hoegaarden => 1 Thanks!
  59. None
  60. http://www.flickr.com/photos/vermininc/2567025965/ http://en.wikipedia.org/wiki/File:Orange_and_cross_section.jpg http://en.wikipedia.org/wiki/File:Sundown_and_cross_section.jpg http://www.flickr.com/photos/hadesigns/3222682575/ http://www.flickr.com/photos/fitzebwoy/3046007179/