Skip to content

Instantly share code, notes, and snippets.

@rcrowley
Created September 21, 2011 17:58
Show Gist options
  • Save rcrowley/1232809 to your computer and use it in GitHub Desktop.
Save rcrowley/1232809 to your computer and use it in GitHub Desktop.
The three monad laws in Python. Why not?
# The three monad laws in Python. Why not?
# <http://rcrowley.org/2011/09/21/monads.html>
# <http://homepages.inf.ed.ac.uk/wadler/topics/monads.html#essence>
# A monad is a wrapper. Anyone who tells you otherwise is being obtuse.
class M(object):
def __init__(self, a):
self.a = a
def __repr__(self):
return '<M a: %s>' % self.a
# A monad's constructor, named unitM here to remain consistent with the
# source material and non-object-oriented languages.
def unitM(a):
print('# unitM(%s)' % a)
return M(a)
# bindM unpacks the value within a monad and passes it to a function which
# can do anything it likes, so long as it repacks the result into a monad.
def bindM(m, k):
print('# bindM(%s, %s)' % (m, k))
return k(m.a)
# Identity definitions of the symbols used in section 2.10.
a = None
m = unitM(a)
def k(a):
print('# k(%s)' % a)
return unitM(a)
def h(a):
print('# h(%s)' % a)
return unitM(a)
# Packing a value via unitM and passing that to bindM is equivalent to calling
# k directly with the original value. This is known as the left unit identity.
print('Left unit: %s == %s' % (bindM(unitM(a), k), k(a)))
# Passing unitM in k's place trivially repacks the value in m, amounting to a
# monad equivalent to m. This is known as the right unit identity.
print('Right unit: %s == %s' % (bindM(m, unitM), m))
# For distinct functions k and h (here both identities), the order in which
# the bindM operations are applied is not important: bindM is associative.
print('Associative: %s == %s' % (bindM(m, lambda a: bindM(k(a),
lambda b: h(b))),
bindM(bindM(m, lambda a: k(a)),
lambda b: h(b))))
# The indirections of k and h as presented in the paper don't make any
# sense when reproduced in Python. If I knew Haskell, I could do more than
# speculate but the lazy evaluation semantics of Haskell must use those
# indirections to alter the order of operations to exhibit associativity.
# This refactored Python version demonstrates the dual forms but does not
# change anything about the order of operations.
print('Associative (direct): %s == %s' % (bindM(m, lambda a: bindM(k(a), h)),
bindM(bindM(m, k), h)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment