RosettaCodeData/Task/Monads-List-monad/Python/monads-list-monad.py

61 lines
1.5 KiB
Python
Raw Permalink Normal View History

2023-07-01 11:58:00 -04:00
"""A List Monad. Requires Python >= 3.7 for type hints."""
from __future__ import annotations
from itertools import chain
from typing import Callable
from typing import Iterable
from typing import List
from typing import TypeVar
T = TypeVar("T")
2024-03-06 22:25:12 -08:00
U = TypeVar("U")
2023-07-01 11:58:00 -04:00
class MList(List[T]):
@classmethod
def unit(cls, value: Iterable[T]) -> MList[T]:
return cls(value)
2024-03-06 22:25:12 -08:00
def bind(self, func: Callable[[T], MList[U]]) -> MList[U]:
2023-07-01 11:58:00 -04:00
return MList(chain.from_iterable(map(func, self)))
2024-03-06 22:25:12 -08:00
def __rshift__(self, func: Callable[[T], MList[U]]) -> MList[U]:
2023-07-01 11:58:00 -04:00
return self.bind(func)
if __name__ == "__main__":
2024-03-06 22:25:12 -08:00
# Chained int and string functions.
2023-07-01 11:58:00 -04:00
print(
MList([1, 99, 4])
.bind(lambda val: MList([val + 1]))
.bind(lambda val: MList([f"${val}.00"]))
)
# Same, but using `>>` as the bind operator.
print(
MList([1, 99, 4])
>> (lambda val: MList([val + 1]))
>> (lambda val: MList([f"${val}.00"]))
)
2024-03-06 22:25:12 -08:00
# Cartesian product of [1..5] and [6..10].
2023-07-01 11:58:00 -04:00
print(
MList(range(1, 6)).bind(
lambda x: MList(range(6, 11)).bind(lambda y: MList([(x, y)]))
)
)
2024-03-06 22:25:12 -08:00
# Pythagorean triples with elements between 1 and 25.
2023-07-01 11:58:00 -04:00
print(
MList(range(1, 26)).bind(
lambda x: MList(range(x + 1, 26)).bind(
lambda y: MList(range(y + 1, 26)).bind(
lambda z: MList([(x, y, z)])
if x * x + y * y == z * z
else MList([])
)
)
)
)