Create a gist now

Instantly share code, notes, and snippets.

Generalized monad comprehensions in Python
import ast
app = lambda name, *args: \
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: \
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(generator) for generator in node.generators])
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(, 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')
'__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([])
'__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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment