RosettaCodeData/Task/Monads-Maybe-monad/Python/monads-maybe-monad.py
2026-02-01 16:33:20 -08:00

95 lines
2.4 KiB
Python

from __future__ import annotations
from abc import ABC
from abc import abstractmethod
from typing import Any
from typing import Callable
class Maybe[T](ABC):
@abstractmethod
def is_some(self) -> bool:
"""Return `True` if the option is a `Some`."""
@abstractmethod
def is_none(self) -> bool:
"""Return `True` if the option is a `Nothing`."""
@abstractmethod
def bind[U](self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
"""Apply `func` to this value if it's a `Some`."""
def and_then[U](self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
"Alias for `bind`."
return self.bind(func)
def __rshift__[U](self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
"Alias for `bind`."
return self.bind(func)
@abstractmethod
def or_else(self, func: Callable[[], Maybe[T]]) -> Maybe[T]:
"""Return self if it's a `Some`, otherwise the result of `func`."""
class Some[T](Maybe[T]):
def __init__(self, value: T) -> None:
self.value = value
def __str__(self) -> str:
return f"Some({self.value!r})"
def is_some(self) -> bool:
return True
def is_none(self) -> bool:
return False
def bind[U](self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
return func(self.value)
def or_else(self, func: Callable[[], Maybe[T]]) -> Maybe[T]:
return self
class Nothing[T](Maybe[T]):
def __str__(self) -> str:
return "Nothing()"
def is_some(self) -> bool:
return False
def is_none(self) -> bool:
return True
def bind[U](self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
return NOTHING
def or_else(self, func: Callable[[], Maybe[T]]) -> Maybe[T]:
return func()
NOTHING = Nothing[Any]()
if __name__ == "__main__":
def plus_one(value: int) -> Maybe[int]:
return Some(value + 1)
def currency(value: int) -> Maybe[str]:
return Some(f"${value}.00")
values: list[Maybe[int]] = [Some(1), Some(99), NOTHING, Some(4)]
# Using `>>` as a bind operator
for value in values:
result = value >> plus_one >> currency
print(f"{value} -> {result}")
print("---")
# The same, but using lambda functions with the bind method.
for value in values:
result = value.bind(lambda v: Some(v + 1)).bind(lambda v: Some(f"${v}.00"))
print(f"{value} -> {result}")