public
Last active

Generalized monad comprehensions in Python

  • Download Gist
monad.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
import ast
 
app = lambda name, *args: \
ast.Call(
func=ast.Name(id=name, ctx=ast.Load(), lineno=0, col_offset=0),
args=list(args), keywords=[], vararg=None,
lineno=0, col_offset=0)
 
 
abs = lambda arg, body: \
ast.Lambda(
args=ast.arguments(args=[arg], defaults=[]),
body=body, lineno=0, col_offset=0)
 
arg = lambda id: ast.Name(id=id, ctx=ast.Param(), lineno=0, col_offset=0)
 
 
class MonadTransformer(ast.NodeTransformer):
def visit_ListComp(self, node):
return self.modify(
rewrite(node.elt),
[rewrite(generator) for generator in node.generators])
 
@staticmethod
def modify(elt, generators):
# Rewrite rules:
# [e] => __return__(e)
# [e for e in (etc)] => __bind__(e, lambda p: [e (etc)])
# (or something along those lines :-D)
 
elt = app('__return__', elt)
 
for generator in reversed(generators):
if generator.ifs:
raise NotImplementedError
 
elt = app(
'__bind__', generator.iter,
abs(arg(generator.target.id), elt))
 
return elt
 
 
def rewrite(ast_):
return MonadTransformer().visit(ast_)
 
def main():
code = '''
 
def f(l):
return [a * 2 for a in l]
'''
 
ast_ = compile(code, '<string>', 'exec', ast.PyCF_ONLY_AST)
new = rewrite(ast_)
ctx = compile(new, '<string>', 'exec')
 
LIST_MONAD = {
'__return__': lambda v: [v],
'__bind__': lambda m, f: [z for l in m for z in f(l)],
}
 
list_local = {}
exec ctx in LIST_MONAD, list_local
 
fl = list_local['f']
print 'fl([1, 2, 3] =', fl([1, 2, 3])
print 'fl([]) =', fl([])
 
MAYBE_MONAD = {
'__return__': lambda v: v,
'__bind__': lambda m, f: f(m) if m is not None else None,
}
 
maybe_local = {}
exec ctx in MAYBE_MONAD, maybe_local
 
fm = maybe_local['f']
print 'f(123) =', fm(123)
print 'f(None) =', fm(None)
 
main()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.