Applicative

This commit is contained in:
Correl Roush 2018-10-12 14:09:16 -04:00
parent 7ce173e829
commit 1a87d73a5b
6 changed files with 57 additions and 5 deletions

View file

@ -1,4 +1,5 @@
from .functor import Functor
from .applicative import Applicative
from .monad import Monad
from .maybe import Maybe, Just, Nothing
from .result import Result, Ok, Err

20
monads/applicative.py Normal file
View file

@ -0,0 +1,20 @@
from __future__ import annotations
from typing import Any, Callable, TypeVar
from .functor import Functor
T = TypeVar("T")
S = TypeVar("S")
class Applicative(Functor[T]):
@classmethod
def pure(cls, value: T) -> Applicative[T]:
raise NotImplementedError
# FIXME: Functor type set to Any, as the proper value
# (Functor[Callable[[T], S]]) is reported as incompatible with subclass
# implementations due to a flaw in mypy:
# https://github.com/python/mypy/issues/1317
def apply(self, functor: Any) -> Functor[S]:
raise NotImplementedError

View file

@ -30,6 +30,13 @@ class Maybe(Monad[T]):
new: Maybe[S] = Nothing()
return new
def apply(self, functor: Maybe[Callable[[T], S]]) -> Maybe[S]:
if isinstance(functor, Just):
return self.map(functor.value)
else:
new: Maybe[S] = Nothing()
return new
def withDefault(self, default: T) -> T:
if isinstance(self, Just):
return self.value

View file

@ -1,17 +1,14 @@
from __future__ import annotations
from typing import Any, Callable, Generic, TypeVar
from .applicative import Applicative
from .functor import Functor
T = TypeVar("T")
S = TypeVar("S")
class Monad(Functor[T]):
@classmethod
def pure(cls, value: T) -> Monad[T]:
raise NotImplementedError
class Monad(Applicative[T]):
# FIXME: Callable return type set to Any, as the proper value
# (Monad[S]) is reported as incompatible with subclass
# implementations due to a flaw in mypy:

View file

@ -41,6 +41,15 @@ class Result(Monad[T], Generic[T, E]):
else:
raise TypeError
def apply(self, functor: Result[Callable[[T], S], E]) -> Result[S, E]:
if isinstance(functor, Ok):
return self.map(functor.value)
elif isinstance(functor, Err):
new: Result[S, E] = Err(functor.err)
return new
else:
raise TypeError
def withDefault(self, default: T) -> T:
if isinstance(self, Ok):
return self.value

View file

@ -0,0 +1,18 @@
import pytest # type: ignore
from typing import Callable, Type, TypeVar
from monads import Applicative, Functor, Maybe, Result
T = TypeVar("T")
S = TypeVar("S")
@pytest.fixture(scope="module", params=[Maybe, Result])
def monad(request) -> Type:
return request.param
def test_fmap_using_ap(monad) -> None:
f: Callable[[int], int] = lambda x: x + 1
m: Applicative[int] = monad.pure(3)
assert m.map(f) == m.apply(monad.pure(f))