typesafe-monads/monads/future.py

65 lines
2 KiB
Python

from __future__ import annotations
import functools
from typing import Awaitable, Callable, Iterable, List, TypeVar, Union
from .monad import Monad
T = TypeVar("T")
S = TypeVar("S")
class Future(Monad[T]):
"""Wraps an Awaitable in a Monad.
The resulting Future object is, itself, Awaitable.
"""
def __init__(self, awaitable: Awaitable[T]) -> None:
self.awaitable = awaitable
@classmethod
def pure(cls, value: T) -> Future[T]:
async def identity(x: T) -> T:
return x
return Future(identity(value))
def map(self, function: Callable[[T], S]) -> Future[S]:
async def map(f: Callable[[T], S], x: Awaitable[T]) -> S:
x_ = await x
return f(x_)
return Future(map(function, self.awaitable))
def apply(self, functor: Awaitable[Callable[[T], S]]) -> Future[S]:
async def apply(f: Awaitable[Callable[[T], S]], x: Awaitable[T]) -> S:
f_ = await f
x_ = await x
return f_(x_)
return Future(apply(functor, self.awaitable))
def bind(self, function: Callable[[T], Awaitable[S]]) -> Future[S]:
async def bind(f: Callable[[T], Awaitable[S]], x: Awaitable[T]) -> S:
x_ = await x
y = await function(x_)
return y
return Future(bind(function, self.awaitable))
@classmethod
def sequence(cls, xs: Iterable[Awaitable[T]]) -> Future[List[T]]:
"""Evaluate monadic actions in sequence, collecting results."""
def mcons(acc: Future[List[T]], x: Awaitable[T]) -> Future[List[T]]:
future: Future[T] = x if isinstance(x, Future) else Future(x)
return acc.bind(lambda acc_: future.map(lambda x_: acc_ + [x_]))
empty: Future[List[T]] = cls.pure([])
return functools.reduce(mcons, xs, empty)
def __await__(self):
return self.awaitable.__await__()
__rshift__ = bind
__and__ = lambda other, self: Future.apply(self, other)
__mul__ = __rmul__ = map