mirror of
https://github.com/correl/typesafe-monads.git
synced 2024-11-21 19:18:42 +00:00
Applicative
This commit is contained in:
parent
7ce173e829
commit
1a87d73a5b
6 changed files with 57 additions and 5 deletions
|
@ -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
20
monads/applicative.py
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
18
tests/test_applicatives.py
Normal file
18
tests/test_applicatives.py
Normal 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))
|
Loading…
Reference in a new issue