Skip to content

Instantly share code, notes, and snippets.

@juliobetta
Last active June 22, 2022 19:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juliobetta/91309b4fb52e3dbe563b3d4ad02d5fa0 to your computer and use it in GitHub Desktop.
Save juliobetta/91309b4fb52e3dbe563b3d4ad02d5fa0 to your computer and use it in GitHub Desktop.
Maybe Monad in Python (naive approach)
from abc import ABC
from typing import Callable, TypeVar, Generic
T = TypeVar('T')
class Maybe(Generic[T], ABC):
def __init__(self, value: T | None):
self.value = value
@staticmethod
def of(value: T | None) -> 'Maybe[T]': ...
def map(self, f: Callable[[T], 'Maybe[T]']) -> T: ...
def flat_map(self, f: Callable[[T], 'Maybe[T]']) -> T | None: ...
def __eq__(self, other):
return self.value == other.value
class Empty(Maybe):
def __init__(self):
super().__init__(None)
@staticmethod
def of(value): return Empty()
def map(self, f): return self
def flat_map(self, f): return None
def __repr__(self): return f"Empty()"
class Some(Maybe):
@staticmethod
# def of(value): return Some(value) if value is not None else Empty()
def of(value): return Some(value)
def map(self, f): return self.flat_map(lambda x: Some.of(f(x))) if self.value is not None else Empty()
def flat_map(self, f): return f(self.value) if self.value is not None else None
def __repr__(self): return f"Some({self.value})"
def main(number: Maybe):
value_ = number.map(lambda x: x + 1)
f_value = number.flat_map(lambda x: x + 1)
m_value = number.map(lambda x: x + 1).map(lambda x: x + 2).map(lambda x: x + 3)
matched = None
match value_:
case Empty(): matched = 'matched: I am Empty'
case Some(value=v): matched = f'matched: I am value {v}'
return f_value, m_value, matched
if __name__ == '__main__':
print(main(Some(None))) # -> (None, Empty(), 'matched: I am Empty')
print(main(Empty())) # -> (None, Empty(), 'matched: I am Empty')
print(main(Some(10))) # -> (11, Some(16), 'matched: I am value 11')
def f_(x): return Some(x + 1)
def g_(x): return Some(x * 2)
# Proving the Monad Laws:
# 1. Left identity:
assert Some(10).flat_map(f_) == f_(10)
# 2. Right identity:
assert Some(10).flat_map(Some.of) == Some(10)
# 3. Associativity:
assert Some(10).flat_map(g_).flat_map(f_) == Some(10).flat_map(lambda x: g_(x).flat_map(f_))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment