Skip to content

Instantly share code, notes, and snippets.

@dhilst
Created June 17, 2023 17:06
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 dhilst/a09a67b526c6cd927367626a1388f477 to your computer and use it in GitHub Desktop.
Save dhilst/a09a67b526c6cd927367626a1388f477 to your computer and use it in GitHub Desktop.
Py Monad Protocol
from typing import *
from typing_extensions import ParamSpec
A = TypeVar("A")
B = TypeVar("B")
C = TypeVar("C")
D = TypeVar("D")
E = TypeVar("E")
F = TypeVar("F")
G = TypeVar("G")
T = TypeVar("T")
A_co = TypeVar("A_co", covariant=True)
B_co = TypeVar("B_co", covariant=True)
C_co = TypeVar("C_co", covariant=True)
D_co = TypeVar("D_co", covariant=True)
E_co = TypeVar("E_co", covariant=True)
F_co = TypeVar("F_co", covariant=True)
G_co = TypeVar("G_co", covariant=True)
T_co = TypeVar("T_co", covariant=True)
A_ct = TypeVar("A_ct", contravariant=True)
B_ct = TypeVar("B_ct", contravariant=True)
C_ct = TypeVar("C_ct", contravariant=True)
D_ct = TypeVar("D_ct", contravariant=True)
E_ct = TypeVar("E_ct", contravariant=True)
F_ct = TypeVar("F_ct", contravariant=True)
G_ct = TypeVar("G_ct", contravariant=True)
T_ct = TypeVar("T_ct", contravariant=True)
class Monad(Protocol[A_co]):
def bind(self, f: Callable[[A_ct], "Monad[B_co]"]) -> "Monad[B_co]":
...
class Opt(Protocol[A_co]):
def bind(self, f: Callable[[A_ct], "Opt[B_co]"]) -> "Opt[B_co]":
...
class Some(Opt[A]):
def __init__(self, v: A_co):
self._v = v
def bind(self, f: Callable[[A_ct], "Opt[B_co]"]) -> "Opt[B_co]":
return f(self._v)
class Nothing(Opt[A]):
def bind(self, f: Callable[[A_ct], "Opt[B_co]"]) -> "Opt[B_co]":
return cast(Opt[B_co], self)
class Result(Protocol[A_co, E]):
def bind(self, f: Callable[[A_ct], "Result[B_co, E]"]) -> "Result[B_co, E]":
...
class Ok(Result[A, E]):
def __init__(self, v: A_co):
self._v = v
def bind(self, f: Callable[[A_ct], "Result[B_co, E]"]) -> "Result[B_co, E]":
return f(self._v)
class Err(Result[A, E]):
def __init__(self, error: E):
self._error = error
def bind(self, f: Callable[[A_ct], "Result[B_co, E]"]) -> "Result[B_co, E]":
return cast(Result[B_co, E], self)
P = ParamSpec("P")
def try_(f: Callable[P, B_co]) -> Callable[P, Result[B_co, Exception]]:
def inner(*args: P.args, **kwargs: P.kwargs) -> Result[B_co, Exception]:
try:
return Ok(f(*args))
except Exception as e:
return Err(e)
return inner
def foo(m: Monad[A]):
...
foo(Some(1))
foo(Nothing())
foo(Ok(1))
foo(Err(1))
def div(a: float, b: float) -> float:
return a / b
# try_(div)(0, "hi") # error
try_(div)(1, 0) # ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment