Very nice clear article! Very much cleared up the idea of monad for me.
Python dataclasses
have become my go-to for creating contexts, so let me translate your code into that:
@dataclasses.dataclass(frozen=True)
class Failure:
value: Any
failed: bool = False
def __or__(self, f):
if self.failed:
return self
try:
x = f(self.get())
return Failure(x)
except:
return Failure(None, True)
The dataclass
implements a good __str__
for you.
value
and failed
are immutable and part of the public API, so there’s no need for the two accessors.
And given that this class now literally only does one thing, there’s no need to have both bind()
and __or__()
.
So you end up with no boring bits at all, just the monad logic.
If you start using dataclass
more, might I humbly suggest my datacls
library which is a very thin wrapper around it that files off a few sharp edges? It now incorporates dtyper
, my decorator that constructs either callable functions or dataclasses from typer
declarations. (And if you write CLIs and don't use typer
then you really should!)