Skip to content

Instantly share code, notes, and snippets.

@nkpart
Last active August 29, 2015 13:56
Show Gist options
  • Save nkpart/9128793 to your computer and use it in GitHub Desktop.
Save nkpart/9128793 to your computer and use it in GitHub Desktop.
Ruby semigroups
class LotsOfData < Struct.new(:field1, :field2, :field3, :field4, :value)
def as_accumulation
# Group by field1, then the pair of field2 and field3, then field4, finally summing all the values
{ field1 => { [field2, field3] => { field4 => Plus.new(value) } } }
end
end
many_data = [
LotsOfData.new(:a, :b, :c, :d, 15),
LotsOfData.new(:a, :b, :c, :d, 20),
LotsOfData.new(:a, :b, :c, :e, 15)
]
many_data.fold_map({}) { |v| v.as_accumulation }
# => {:a=>{[:b, :c]=>{:d=>#<struct Plus underlying=35>, :e=>#<struct Plus underlying=15>}}}
# >> Plus.new(3).mappend(Plus.new(5)).underlying
# => 8
class Plus < Struct.new(:underlying)
def mappend other
Plus.new(underlying + other.underlying)
end
end
class Product < Struct.new(:underlying)
def mappend other
Product.new(underlying * other.underlying)
end
end
class Hash
# Hashes are straight up merged, resolving key clashes by mappend'ing the values.
# Values must have mappend.
# ie. Semigroup a => Semigroup (Hash k a)
#
# doctest: hash mappend
# >> { :a => Plus.new(3) }.mappend({ :a => Plus.new(1) })
# => { :a => Plus.new(4) }
def mappend other
merge(other) { |_, a, b| a.mappend(b) }
end
# Oh god what have I become, a mutable mappend.
def mappend! other
merge!(other) { |_, a, b| a.mappend(b) }
end
end
class ZipList < Struct.new(:underlying)
# A zip list combines values pair-wise.
# Values must have #mappend
# ie. Semigroup a => Semigroup (ZipList [a])
#
# doctest: zip list mappend
# >> def pluses(*xs); xs.map { |x| Plus.new(x) }; end
# >> ZipList.new(pluses(1,2)).mappend(ZipList.new(pluses(3,4))).underlying
# => pluses(4,6)
def mappend other
ZipList.new(underlying.zip(other.underlying).map { |(a,b)| a.mappend(b) })
end
end
module Enumerable
def fold_map(initial)
inject(initial) { |m, v| m.mappend(yield v) }
end
def mconcat(initial)
fold_map(initial) { |v| v }
end
def mconcat1
inject { |m, v| m.mappend(v) }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment