I tried to implement a monad before reading about them, I just had a 'feeling' about the concept of The Monad.
I read on All about Monads that Just and Nothing are functions, not types of Monads, but I thought thought that each Maybe monad would eventually collapse into a Just or Nothing monad (or that's what I wrote) when it's bound to another value.
Is my understanding wrong? Also, in this case I made the Monad return itself via bind, but maybe I should just return the unwrapped value and that bind is already a comp.
This is what I wrote:
from abc import ABCMeta
class Monad(metaclass = ABCMeta):
def __init__(self, value = None) -> None:
self._unit = value
@property
def unit(self):
return self._unit
@unit.setter
def unit(self, value):
self._unit = value
def __repr__(self) -> str:
return f'{self.__class__.__name__} ( {self._unit} )'
class Maybe(Monad):
def bind(self, function):
self.unit = function(self._unit) if self.unit is not None else None
self.__class__ = Nothing if self.unit is None else Just
return self
class Nothing(Maybe):
pass
class Just(Maybe):
pass
a = Maybe(1)
b = Maybe(1)
b.bind(lambda x: x + 1).bind(lambda x: x + 1).bind(lambda x: x + 1).bind(lambda x: x + 1)
c = Maybe()
c.bind(lambda x: x + 1).bind(lambda x: x + 1).bind(lambda x: x + 1)
print(a, a.unit)
print(b, b.unit)
print(c, c.unit)
I still think this is ugly and I need to make it better... but I just want to understand if I'm correct about the concepts
You can't fully translate Monads to a dynamic language, and you can't even fully express them in most statically typed languages (you need something like HKTs, which few languages support). You also in this particular case need tagged unions.
No, they aren't. They are type constructors, and there is (AFAIK) no analog in Python (even with the built in type hinting). Type constructors are similar to functions, but functions operate on values: you can take values, return values, etc. Type constructors operate on types: you can take a type as an argument, and you return a new type.
So the
Maybetype is a tagged union of types: aMaybe some_typeis eitherJust some_typeorNothing. N.B. that the type isJust some_typethe type created by applying the type constructorJustto the typesome_type, notJustorsome_type.Statically typed compiled languages have two "levels" if you will: the type level which exists at compile-time and the term or value level that exists at runtime. Dynamic interpreted languages like Python only have the second level, and Monads in Haskell exist at the first level, which is part of why it's so hard to understand them coming from that perspective. The water is further muddied in OOP languages, because classes exist at both levels: when we say
class Foowe're defining both a runtime operation and a compile-time type (usually the type of the instances ofFoo).So what even is a Monad? I'm going to crib from this excellent explanation by Eric Lippert:
Note that when he says "Monadic value" he's talking about a value that has the type of the Monad.
So it's going to be difficult to translate all this to Python (or Ruby, or Javascript, or...). If you've ever programmed in any statically typed compiled language you're going to get more mileage there in trying to understand Monads, even if that language's type system isn't powerful enough to actually express Monads (e.g. Java, C#, Typescript).
Edit
Based on comments, clarifying Monads in other statically typed compiled languages: you absolutely can create a Monad in a lot of statically typed languages. For example, Maybe in Typescript:
There's the Maybe monad in Typescript. The problem is that Typescript's type system (AFAIK) isn't powerful enough to abstract out that pattern that says "this generic type implements this contract for all types which may themselves be generic". My Maybe example is a Monad, but Typescript doesn't let me describe it as such or enforce it via the type system, it's on the programmer to make sure that every monad they create in TS upholds the contract rather than having the compiler check it.
So what might the Maybe Monad look like in Python? Probably something like this:
Here the
unitoperation is callingJust, thebindoperation is a function,Nothingis a Singleton value and the type from the class (I can't figure out how to do a unique uninhabited type in Python). That's as close as I could get, but you hopefully get the idea. Writing the function composition tests to ensure it upholds the Monad laws is left as an exercise to the reader and totally not omitted because it's Saturday morning and I want to go play video games.