monad_comprehension.py enables you to perform monadic computations with the list comprehension syntax, as suggested by this article. It is powered by ast transformations.
It is known to work with Python 3.6, a fix is needed for newer versions (should come soon).
Let's define the Maybe monad like this:
class Maybe(object):
def __init__(self, value):
self.value = value
@classmethod
def unit(cls, value):
return cls(value)
@classmethod
def nothing(cls):
return cls(None)
def is_nothing(self):
return self.value is None
def bind(self, f):
if self.value is None:
return self
return f(self.value)
def map(self, f):
if self.value is None:
return self
new_value = f(self.value)
return Maybe.unit(new_value)
def __str__(self):
if self.is_nothing():
return f"Nothing"
return f"Just {self.value}"
We can then adapt the semantics of the comprehension syntax with this:
@monad_comprehension(Maybe)
def f():
return [
(x + y)
for x in Maybe(5)
for y in Maybe(6)
]
f() # outputs `Just 11`
@monad_comprehension(Maybe)
def g():
return [
(x + y)
for x in Maybe(5)
for y in Maybe.nothing()
]
g() # outputs `Nothing`
You can provide any class which implements unit
and bind
methods to the monad_comprehension
decorator.
This is still in a very experimental stage, and is probably not ready yet for production use.
This is inspired by this blog post. Also, this idea of monad comprehension is very similar to Haskell's do notation.
Besides, this is derived for this talk I gave at FOSDEM 2020.