680

# 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. July 08, 2009

## Transcript

@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
because mathematicians are
programmers too

4. Practice:
Use the VectorSpace library to
model multidimensional values

5. Theory:

6. WIKIPEDIA
Abstract algebra
Abstract algebra is the subject
area of mathematics that studies
algebraic structures, such as
groups, rings, ﬁelds, modules,
vector spaces, and algebras.

7. WIKIPEDIA
Algebraic structure
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

The idea of encapsulating
some well-deﬁned
operations is familiar

13. A total order is a binary
relation (here denoted by
inﬁx ≤) on some set X. A set
paired with a total order is
called a totally ordered set.
WIKIPEDIA
Total order

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 irreﬂexive) relation <, called a strict
total order, which can equivalently be deﬁned 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. You deﬁne #<=>
You get
#<, #<=, #==, #>= and #>

18. (P.S. deﬁne #<=> instead of
#<= because it guarantees
some axioms and makes
implementation more efﬁcient)

19. ok so

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

21. We shouldn’t be afraid
to look further aﬁeld
for other structures
that might help us out

22. Practice:

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

24. WIKIPEDIA
Abstract algebra
Abstract algebra is the subject
area of mathematics that studies
algebraic structures, such as
groups, rings, ﬁelds, modules,
vector spaces, and algebras.

25. Given a ﬁeld F (the “scalars”)

26. A vector space over F is a set
equipped with two binary
operations:
elements of the set), and
scalar multiplication (between
an element of the set and a scalar)

27. + =

28. × =
2

29. Axiom Signiﬁcation
Associativity of addition u + (v + w) = (u + v) + w.
Commutativity of addition v + w = w + v.
There exists an element 0 ∈ V, called the
zero vector, such that v + 0 = v for all v ∈ V.
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
a(v + w) = av + aw.
Distributivity of scalar multiplication with
(a + b)v = av + bv.
Compatibility of scalar multiplication with ﬁeld
multiplication
a(bv) = (ab)v
Identity element of scalar multiplication
1v = v, where 1 denotes the multiplicative
identity in F.

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

31. ok
boring

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

33. 5?
6?
+ =
× 2 =

34. + =
× 2 =

35. – =
÷ 2 =

36. VectorSpace mixin
(assuming constructors
and getters)

37. VectorSpace::SimpleVector
class
(for convenience)

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

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

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

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

42. VectorSpace::Family
mixin
VectorSpace::SimpleIndexedVector
class

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

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

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

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

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

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

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

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

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

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

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

54. ok
that’s it

55. If you liked this: