Let's think about the Types here
A monoid takes type X and returns type X!
(fancy math word is closure, because we really needed another thing in CS called closure....)
You can chain things together!
(you've probably been doing this for a long time)
For strings!
connection_string = host + ":" + port + "/" resource_path
For fluent APIs!
email_to_send.set_to(['foo@foo.com']).set_from('bar@bar.com')
For IF control flow!
if x == true or y == true: ...
Think about the general rule
We need to have a concept of nothing!
Or stated more like a math person, 'we need something to give our function, in addition to a "real" argument, that will always give back the same "real" argument' (called identity)
A lot of annoying problems become easy
(you've probably been doing this already)
inter_cart = cart_1.add_items(cart_2)
inter_cart.add_items(cart_3)
We can group the order however we want (associativity)
Huge jobs can become many small jobs
(you probably already do this)
- make it smaller, count one page at a time
- make it parallel, you count page 1, I count page two
- make it incremental, I counted pages 1-5 today, tomorrow I can count page 6 and not recount 1-5
~A monoid is both a function and a type~
**So much talk about things I already do Phil! Excite me!**
What is reduce?
Reduce is like an aggregator, it takes a function, an iterable, and then accumulates the iterable using the function, example:
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
Many implementations look like this ((((1+2)+3)+4)+5)
So if we have the problem "Sum all prices in our shopping cart", we wonder, "Can we use reduce function and think with monoids?!"
shopping_cart = [1.12, 5.23, 9.99, 62.11, 12.12]
Rule #1 (closure) We can chain safely!
1.12 + 5.23 + 9.99 + 62.11 + 12.12
Rule #2 (identity)
We don't need it yet
Rule #3 (associativity)
((((1.12 + 5.23) + 9.99) + 62.11) + 12.12)
So we know we can use reduce here, because we satisfy our monoid properties.
But wait, there's more!
We've told our library we want to reduce, not *how* to reduce, what if 1.12 + 5.23 + 9.99 + 62.11 + 12.12 could be run on many servers?
#3 1.12 + 5.23 + 9.99 + 62.11 + 12.12 = (1.12 + 5.23) + (9.99 + 62.11) + 12.12
Server A: 1.12 + 5. 23
Server B: 9.99 + 62.11
Server C: 12.12 + 0 <-- Rule #2 helps us :)
That's great but I have a list of shopping cart items, not floats!
Map function is our friend here, it takes each object in our collection and runs a function on it. We can use it to TRANSFORM OUR BORING OBJECTS INTO MONOIDS!!!!!
class shopping_item(object):
def __init__(self, price):
self.price = price
cart = [shopping_item(1.12), shopping_item(5.23), shopping_item(9.99)]
cart_float = map(lambda shop_item: shop_item.price, cart)
total = reduce(lambda x, y: x + y, cart_float)
My coworker Matthew Wampler-Doty who was so excited that I was going to give a talk on monoids in python, that he started e-mailing me examples he wrote.