"""List of values
.. autosummary::
:toctree:
List
"""
import attr
import functools
from hypothesis import strategies as st
from haskpy.typeclasses import Monad, Monoid, Foldable, Eq
from haskpy import testing
from haskpy.internal import (
immutable,
class_property,
class_function,
)
from haskpy.testing import eq_test
[docs]@immutable(init=False)
class List(Monad, Monoid, Foldable, Eq):
__xs = attr.ib(converter=tuple)
def __init__(self, *xs):
object.__setattr__(self, "_List__xs", tuple(xs))
return
[docs] def map(self, f):
"""List a -> (a -> b) -> List b"""
return List(*(f(x) for x in self.__xs))
[docs] @class_function
def pure(cls, x):
"""a -> List a"""
return cls(x)
[docs] def apply(self, fs):
"""List a -> List (a -> b) -> List b"""
return List(*(y for f in fs.__xs for y in self.map(f).__xs))
[docs] def bind(self, f):
"""List a -> (a -> List b) -> List b"""
return List(*(y for ys in self.map(f).__xs for y in ys.__xs))
[docs] def __eq__(self, other):
"""List a -> List a -> bool"""
return self.__xs == other.__xs
[docs] @class_property
def empty(cls):
"""Empty list, type ``List a``"""
return cls()
[docs] def append(self, xs):
"""List a -> List a -> List a"""
return List(*self.__xs, *xs.__xs)
[docs] def to_iter(self):
yield from self.__xs
[docs] @class_function
def from_iter(cls, xs):
"""Iterable f => f a -> List a"""
return cls(*xs)
[docs] def length(self):
return len(self.__xs)
[docs] def elem(self, e):
return e in self.__xs
[docs] def foldl(self, combine, initial):
"""List a -> (b -> a -> b) -> b -> b
``combine`` function is assumed to be curried
"""
# TODO: We could implement also fold_map to make fold_map and fold to
# use parallelized implementation because they use monoids. Now, the
# default implementations use foldl/foldr which both are sequential.
return functools.reduce(
lambda a, b: combine(a)(b),
self.__xs,
initial,
)
[docs] def foldr(self, combine, initial):
"""List a -> (a -> b -> b) -> b -> b
``combine`` function is assumed to be curried
"""
# TODO: We could implement also fold_map to make fold_map and fold to
# use parallelized implementation because they use monoids. Now, the
# default implementations use foldl/foldr which both are sequential.
return functools.reduce(
lambda b, a: combine(a)(b),
self.__xs[::-1],
initial,
)
def __repr__(self):
return "List{}".format(repr(self.__xs))
#
# Sampling methods for property tests
#
@class_function
def sample_value(cls, a):
return st.lists(a, max_size=10).map(lambda xs: cls(*xs))
sample_type = testing.sample_type_from_value(
testing.sample_type(),
)
sample_functor_type = testing.sample_type_from_value()
sample_applicative_type = sample_functor_type
sample_monad_type = sample_functor_type
sample_semigroup_type = testing.sample_type_from_value(
testing.sample_type(),
)
sample_monoid_type = sample_semigroup_type
sample_eq_type = testing.sample_type_from_value(
testing.sample_eq_type(),
)
sample_foldable_type = testing.sample_type_from_value()
sample_foldable_functor_type = sample_foldable_type
[docs] def __eq_test__(self, other, data=None):
return (
False if len(self.__xs) != len(other.__xs) else
all(
eq_test(x, y, data)
for (x, y) in zip(self.__xs, other.__xs)
)
)