Python でモナド的パターンの実装(Maybe, Either)
関数型プログラミングの Maybe(Option)と Either(Result)パターンを Python で実装すると、エラー処理を型安全かつ合成可能にできます。
Maybe パターン
値が存在するかしないかを表現します。
from typing import TypeVar, Generic, Callable, Optional
from dataclasses import dataclass
T = TypeVar("T")
U = TypeVar("U")
class Maybe(Generic[T]):
pass
@dataclass
class Just(Maybe[T]):
value: T
@dataclass
class Nothing(Maybe[T]):
pass
def map_maybe(m: Maybe[T], f: Callable[[T], U]) -> Maybe[U]:
if isinstance(m, Just):
return Just(f(m.value))
return Nothing()
def bind_maybe(m: Maybe[T], f: Callable[[T], Maybe[U]]) -> Maybe[U]:
if isinstance(m, Just):
return f(m.value)
return Nothing()
使用例:
def safe_div(a: int, b: int) -> Maybe[float]:
if b == 0:
return Nothing()
return Just(a / b)
result = bind_maybe(Just(10), lambda x: safe_div(x, 2))
# Just(value=5.0)
Either パターン
成功と失敗の両方に値を持てます。
from typing import TypeVar, Generic, Callable, Union
from dataclasses import dataclass
L = TypeVar("L") # Left(エラー)
R = TypeVar("R") # Right(成功)
class Either(Generic[L, R]):
pass
@dataclass
class Left(Either[L, R]):
value: L
@dataclass
class Right(Either[L, R]):
value: R
def map_either(e: Either[L, R], f: Callable[[R], U]) -> Either[L, U]:
if isinstance(e, Right):
return Right(f(e.value))
return e
def bind_either(
e: Either[L, R], f: Callable[[R], Either[L, U]]
) -> Either[L, U]:
if isinstance(e, Right):
return f(e.value)
return e
使用例:
def parse_int(s: str) -> Either[str, int]:
try:
return Right(int(s))
except ValueError:
return Left(f"Cannot parse '{s}'")
result = bind_either(
parse_int("42"),
lambda x: Right(x * 2) if x > 0 else Left("Must be positive")
)
# Right(value=84)
これらのパターンにより、None チェックの連鎖や try-except の乱用を避け、エラー処理を宣言的に記述できます。