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

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.

Tom Stuart

July 08, 2009
Tweet

More Decks by Tom Stuart

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. Theory:
    Use ideas from abstract
    algebra in your Ruby programs,
    because mathematicians are
    programmers too

    View Slide

  4. Practice:
    Use the VectorSpace library to
    model multidimensional values
    and get their operations for free

    View Slide

  5. Theory:

    View Slide

  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.

    View Slide

  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.

    View Slide

  8. “sets closed under
    one or more operations”

    View Slide

  9. “sets closed under
    one or more operations”

    View Slide

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

    View Slide

  11. These structures have
    been well-studied and
    are well-understood
    Satisfy the axioms and
    you get some properties

    View Slide

  12. It’s all about abstraction
    The idea of encapsulating
    some well-defined
    operations is familiar

    View Slide

  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

    View Slide

  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).

    View Slide

  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 {<, >, ≤, ≥}.

    View Slide

  16. ok
    boring

    View Slide

  17. View Slide

  18. You define #<=>
    You get
    #<, #<=, #==, #>= and #>
    for free

    View Slide

  19. (P.S. define #<=> instead of
    #<= because it guarantees
    some axioms and makes
    implementation more efficient)

    View Slide

  20. ok so

    View Slide

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

    View Slide

  22. We shouldn’t be afraid
    to look further afield
    for other structures
    that might help us out

    View Slide

  23. Practice:

    View Slide

  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

    View Slide

  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.

    View Slide

  26. Given a field F (the “scalars”)

    View Slide

  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)

    View Slide

  28. + =

    View Slide

  29. × =
    2

    View Slide

  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.

    View Slide

  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.

    View Slide

  32. ok
    boring

    View Slide

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

    View Slide

  34. 5?
    6?
    + =
    × 2 =

    View Slide

  35. + =
    × 2 =

    View Slide

  36. – =
    ÷ 2 =

    View Slide

  37. VectorSpace mixin
    (assuming constructors
    and getters)

    View Slide

  38. VectorSpace::SimpleVector
    class
    (for convenience)

    View Slide

  39. Vector arithmetic (0, +, –, ÷)
    Scalar arithmetic (×, ÷)
    Partial order
    (product, lexicographic)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. View Slide

  44. VectorSpace::Family
    mixin
    VectorSpace::SimpleIndexedVector
    class

    View Slide

  45. View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  49. This is all pretty trivial
    but it makes things
    drastically simpler when
    you need to nest vectors

    View Slide

  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

    View Slide

  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

    View Slide

  52. Money = family of one-
    dimensional vector spaces
    indexed by currency

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  56. Payment = two-
    dimensional vector space,
    underlying values are
    TaxedMoney and Credits

    View Slide

  57. ok
    that’s it

    View Slide

  58. If you liked this:
    • Download VectorSpace from
    http://github.com/tomstuart
    • Follow @tomstuart on Twitter
    • Round.new :hoegaarden => 1
    Thanks!

    View Slide

  59. View Slide

  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/

    View Slide