More monoids

This commit is contained in:
Correl Roush 2018-10-13 02:27:39 -04:00
parent 8b00d44f39
commit a4a46a46c7
5 changed files with 111 additions and 20 deletions

View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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])