mirror of
https://github.com/correl/typesafe-monads.git
synced 2024-11-21 19:18:42 +00:00
More monoids
This commit is contained in:
parent
8b00d44f39
commit
a4a46a46c7
5 changed files with 111 additions and 20 deletions
|
@ -1,5 +1,6 @@
|
|||
from .functor import Functor
|
||||
from .applicative import Applicative
|
||||
from .monad import Monad
|
||||
from .list import List
|
||||
from .maybe import Maybe, Just, Nothing
|
||||
from .result import Result, Ok, Err
|
||||
|
|
36
monads/list.py
Normal file
36
monads/list.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from __future__ import annotations
|
||||
from functools import reduce
|
||||
from itertools import chain
|
||||
from typing import Callable, TypeVar
|
||||
|
||||
from .monad import Monad
|
||||
from .monoid import Monoidal
|
||||
|
||||
T = TypeVar("T")
|
||||
S = TypeVar("S")
|
||||
|
||||
|
||||
class List(Monad[T], Monoidal[list]):
|
||||
@classmethod
|
||||
def pure(cls, value: T) -> List[T]:
|
||||
return List([value])
|
||||
|
||||
def bind(self, function: Callable[[T], List[S]]) -> List[S]:
|
||||
return reduce(List.mappend, map(function, self.value), List.mzero())
|
||||
|
||||
def map(self, function: Callable[[T], S]) -> List[S]:
|
||||
return List(list(map(function, self.value)))
|
||||
|
||||
def apply(self, functor: List[Callable[[T], S]]) -> List[S]:
|
||||
return List(
|
||||
list(chain.from_iterable([map(f, self.value) for f in functor.value]))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def mzero(cls) -> List[T]:
|
||||
return cls(list())
|
||||
|
||||
def mappend(self, other: List[T]) -> List[T]:
|
||||
return List(self.value + other.value)
|
||||
|
||||
__add__ = mappend
|
|
@ -1,7 +1,8 @@
|
|||
from __future__ import annotations
|
||||
from functools import reduce
|
||||
from numbers import Number
|
||||
from typing import Any, Callable, Generic, Iterator, List, Type, TypeVar, Union
|
||||
from numbers import Complex
|
||||
from decimal import Decimal
|
||||
from typing import Any, Callable, Generic, Iterator, Type, TypeVar, Union
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
@ -32,3 +33,41 @@ class Monoid(Generic[T]):
|
|||
)
|
||||
|
||||
__add__ = mappend
|
||||
|
||||
|
||||
class Monoidal(Monoid[T]):
|
||||
def __repr__(self): # pragma: no cover
|
||||
return repr(self.value)
|
||||
|
||||
|
||||
class String(Monoidal[str]):
|
||||
@classmethod
|
||||
def mzero(cls) -> Monoidal:
|
||||
return cls(str())
|
||||
|
||||
def mappend(self, other: String) -> String:
|
||||
return String(self.value + other.value)
|
||||
|
||||
__add__ = mappend
|
||||
|
||||
|
||||
class Addition(Monoidal[Union[int, float]]):
|
||||
@classmethod
|
||||
def mzero(cls) -> Addition:
|
||||
return cls(0)
|
||||
|
||||
def mappend(self, other: Addition) -> Addition:
|
||||
return Addition(self.value + other.value)
|
||||
|
||||
__add__ = mappend
|
||||
|
||||
|
||||
class Multiplication(Monoidal[Union[int, float]]):
|
||||
@classmethod
|
||||
def mzero(cls) -> Multiplication:
|
||||
return cls(1)
|
||||
|
||||
def mappend(self, other: Multiplication) -> Multiplication:
|
||||
return Multiplication(self.value * other.value)
|
||||
|
||||
__add__ = mappend
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import pytest # type: ignore
|
||||
from typing import Type
|
||||
from monads import Maybe, Result
|
||||
from monads import Maybe, List, Result
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", params=[Maybe, Result])
|
||||
@pytest.fixture(scope="module", params=[Maybe, List, Result])
|
||||
def monad(request) -> Type:
|
||||
return request.param
|
||||
|
|
|
@ -1,36 +1,51 @@
|
|||
import pytest # type: ignore
|
||||
from typing import Any, Callable, Type
|
||||
from typing import Any, Callable, Tuple, Type
|
||||
|
||||
from monads.monoid import Monoid
|
||||
from monads.list import List
|
||||
from monads.monoid import Monoid, String, Addition, Multiplication
|
||||
from monads.maybe import First, Last, Just
|
||||
|
||||
Constructor = Callable[[Any], Monoid]
|
||||
Constructor = Tuple[Type, Callable[[Any], Any]]
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module", params=[lambda x: First(Just(x)), lambda x: Last(Just(x))]
|
||||
scope="module",
|
||||
params=[
|
||||
(First, lambda x: Just(x)),
|
||||
(Last, lambda x: Just(x)),
|
||||
(String, lambda x: str(x)),
|
||||
(Addition, lambda x: x),
|
||||
(Multiplication, lambda x: x),
|
||||
(List, lambda x: [x]),
|
||||
],
|
||||
)
|
||||
def monoid(request) -> Constructor:
|
||||
def constructor(request) -> Constructor:
|
||||
return request.param
|
||||
|
||||
|
||||
def test_associative(monoid: Constructor) -> None:
|
||||
a: Monoid = monoid(1)
|
||||
b: Monoid = monoid(2)
|
||||
c: Monoid = monoid(3)
|
||||
def construct(constructor: Constructor, value: Any) -> Monoid:
|
||||
cls, builder = constructor
|
||||
return cls(builder(value))
|
||||
|
||||
|
||||
def test_associative(constructor: Constructor) -> None:
|
||||
a: Monoid = construct(constructor, 1)
|
||||
b: Monoid = construct(constructor, 2)
|
||||
c: Monoid = construct(constructor, 3)
|
||||
|
||||
assert (a + b) + c == a + (b + c)
|
||||
|
||||
|
||||
def test_mconcat_empty(monoid: Constructor) -> None:
|
||||
cls: Type = type(monoid(1))
|
||||
def test_mconcat_empty(constructor: Constructor) -> None:
|
||||
cls, _ = constructor
|
||||
zero: Monoid = cls.mzero()
|
||||
assert zero == cls.mconcat([])
|
||||
|
||||
def test_mconcat(monoid: Constructor) -> None:
|
||||
cls: Type = type(monoid(1))
|
||||
a: Monoid = monoid(1)
|
||||
b: Monoid = monoid(2)
|
||||
c: Monoid = monoid(3)
|
||||
|
||||
def test_mconcat(constructor: Constructor) -> None:
|
||||
cls, _ = constructor
|
||||
a: Monoid = construct(constructor, 1)
|
||||
b: Monoid = construct(constructor, 2)
|
||||
c: Monoid = construct(constructor, 3)
|
||||
expected: Monoid = a.mappend(b).mappend(c)
|
||||
assert expected == cls.mconcat([a, b, c])
|
||||
|
|
Loading…
Reference in a new issue