Created
September 21, 2011 17:58
-
-
Save rcrowley/1232809 to your computer and use it in GitHub Desktop.
The three monad laws in Python. Why not?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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