# Path: oslash/list.py
# class List(Iterable[TSource], Sized):
#     """The list monad.
# 
#     Wraps an immutable list built from lambda expressions.
#     """
# 
#     @classmethod
#     def unit(cls, value: Any) -> 'List':
#         """Wrap a value within the singleton list."""
#         return List.empty().cons(value)
# 
#     pure = unit
# 
#     @classmethod
#     def empty(cls) -> 'List[TSource]':
#         """Create an empty list."""
#         return Nil()
# 
#     @classmethod
#     def from_iterable(cls, iterable: Iterable) -> 'List':
#         """Create list from iterable."""
# 
#         iterator = iter(iterable)
# 
#         def recurse() -> List:
#             try:
#                 value = next(iterator)
#             except StopIteration:
# 
#                 return List.empty()
#             return List.unit(value).append(recurse())
#         return List.empty().append(recurse())
# 
#     @classmethod
#     def concat(cls, xs):
#         """mconcat :: [m] -> m
# 
#         Fold a list using the monoid. For most types, the default
#         definition for mconcat will be used, but the function is
#         included in the class definition so that an optimized version
#         can be provided for specific types.
#         """
# 
#         def reducer(a, b):
#             return a + b
# 
#         return reduce(reducer, xs, Nil())
# 
#     @abstractmethod
#     def head(self) -> TSource:
#         raise NotImplementedError
# 
#     @abstractmethod
#     def tail(self) -> 'List[TSource]':
#         """Return tail of List."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def apply(self, something: 'List') -> 'List':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def map(self, mapper: Callable[[TSource], TResult]) -> 'List[TResult]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def bind(self, fn: Callable[[TSource], 'List[TResult]']) -> 'List[TResult]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def cons(self, element: TSource) -> 'List[TSource]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def append(self, other: 'List[TSource]') -> 'List[TSource]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def null(self) -> bool:
#         """Return True if List is empty."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def __add__(self, other):
#         raise NotImplementedError
# 
# Path: oslash/util/fn.py
# A = TypeVar("A")
# B = TypeVar("B")
# C = TypeVar("C")
# D = TypeVar("D")
# E = TypeVar("E")
# F = TypeVar("F")
# G = TypeVar("G")
# def compose() -> Callable[[A], A]:  # pylint: disable=function-redefined
# def compose(op1: Callable[[A], B]) -> Callable[[A], B]:  # pylint: disable=function-redefined
# def compose(op2: Callable[[B], C], op1: Callable[[A], B]) -> Callable[[A], C]:  # pylint: disable=function-redefined
# def compose(
#     op3: Callable[[C], D], op2: Callable[[B], C], op1: Callable[[A], B]  # pylint: disable=function-redefined
# ) -> Callable[[A], D]:
# def compose(
#     op4: Callable[[D], E],  # pylint: disable=function-redefined
#     op3: Callable[[C], D],
#     op2: Callable[[B], C],
#     op1: Callable[[A], B],
# ) -> Callable[[A], E]:
# def compose(
#     op5: Callable[[E], F],  # pylint: disable=function-redefined
#     op4: Callable[[D], E],
#     op3: Callable[[C], D],
#     op2: Callable[[B], C],
#     op1: Callable[[A], B],
# ) -> Callable[[A], F]:
# def compose(
#     op1: Callable[[A], B],  # pylint: disable=function-redefined,too-many-arguments
#     op2: Callable[[B], C],
#     op3: Callable[[C], D],
#     op4: Callable[[D], E],
#     op5: Callable[[E], F],
#     op6: Callable[[F], G],
# ) -> Callable[[A], G]:
# def compose(*funcs: Callable) -> Callable:  # type: ignore
#     def _compose(source: Any) -> Any:


import unittest
from oslash.list import List
from oslash.util import identity, compose, fmap
        # Singleton list using return
        x = unit(42)
        self.assertEqual(x.map(identity), x)

        # Empty list
        y = empty()
        self.assertEqual(y.map(identity), y)

        # Long list
        z = List.from_iterable(range(42))
        self.assertEqual(z.map(identity), z)

    def test_list_functor_law2(self):
        # fmap (f . g) x = fmap f (fmap g x)
        def f(x):
            return x + 10

        def g(x):
            return x * 10

        # Singleton list
        x = unit(42)
        self.assertEqual(x.map(compose(f, g)), x.map(g).map(f))

        # Empty list
        y = List.empty()
        self.assertEqual(y.map(compose(f, g)), y.map(g).map(f))

        # Long list
        z = List.from_iterable(range(42))

# Path: oslash/list.py
# class List(Iterable[TSource], Sized):
#     """The list monad.
# 
#     Wraps an immutable list built from lambda expressions.
#     """
# 
#     @classmethod
#     def unit(cls, value: Any) -> 'List':
#         """Wrap a value within the singleton list."""
#         return List.empty().cons(value)
# 
#     pure = unit
# 
#     @classmethod
#     def empty(cls) -> 'List[TSource]':
#         """Create an empty list."""
#         return Nil()
# 
#     @classmethod
#     def from_iterable(cls, iterable: Iterable) -> 'List':
#         """Create list from iterable."""
# 
#         iterator = iter(iterable)
# 
#         def recurse() -> List:
#             try:
#                 value = next(iterator)
#             except StopIteration:
# 
#                 return List.empty()
#             return List.unit(value).append(recurse())
#         return List.empty().append(recurse())
# 
#     @classmethod
#     def concat(cls, xs):
#         """mconcat :: [m] -> m
# 
#         Fold a list using the monoid. For most types, the default
#         definition for mconcat will be used, but the function is
#         included in the class definition so that an optimized version
#         can be provided for specific types.
#         """
# 
#         def reducer(a, b):
#             return a + b
# 
#         return reduce(reducer, xs, Nil())
# 
#     @abstractmethod
#     def head(self) -> TSource:
#         raise NotImplementedError
# 
#     @abstractmethod
#     def tail(self) -> 'List[TSource]':
#         """Return tail of List."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def apply(self, something: 'List') -> 'List':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def map(self, mapper: Callable[[TSource], TResult]) -> 'List[TResult]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def bind(self, fn: Callable[[TSource], 'List[TResult]']) -> 'List[TResult]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def cons(self, element: TSource) -> 'List[TSource]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def append(self, other: 'List[TSource]') -> 'List[TSource]':
#         raise NotImplementedError
# 
#     @abstractmethod
#     def null(self) -> bool:
#         """Return True if List is empty."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def __add__(self, other):
#         raise NotImplementedError
# 
# Path: oslash/util/fn.py
# A = TypeVar("A")
# B = TypeVar("B")
# C = TypeVar("C")
# D = TypeVar("D")
# E = TypeVar("E")
# F = TypeVar("F")
# G = TypeVar("G")
# def compose() -> Callable[[A], A]:  # pylint: disable=function-redefined
# def compose(op1: Callable[[A], B]) -> Callable[[A], B]:  # pylint: disable=function-redefined
# def compose(op2: Callable[[B], C], op1: Callable[[A], B]) -> Callable[[A], C]:  # pylint: disable=function-redefined
# def compose(
#     op3: Callable[[C], D], op2: Callable[[B], C], op1: Callable[[A], B]  # pylint: disable=function-redefined
# ) -> Callable[[A], D]:
# def compose(
#     op4: Callable[[D], E],  # pylint: disable=function-redefined
#     op3: Callable[[C], D],
#     op2: Callable[[B], C],
#     op1: Callable[[A], B],
# ) -> Callable[[A], E]:
# def compose(
#     op5: Callable[[E], F],  # pylint: disable=function-redefined
#     op4: Callable[[D], E],
#     op3: Callable[[C], D],
#     op2: Callable[[B], C],
#     op1: Callable[[A], B],
# ) -> Callable[[A], F]:
# def compose(
#     op1: Callable[[A], B],  # pylint: disable=function-redefined,too-many-arguments
#     op2: Callable[[B], C],
#     op3: Callable[[C], D],
#     op4: Callable[[D], E],
#     op5: Callable[[E], F],
#     op6: Callable[[F], G],
# ) -> Callable[[A], G]:
# def compose(*funcs: Callable) -> Callable:  # type: ignore
#     def _compose(source: Any) -> Any:


# Path: tests/test_list.py
import unittest
from oslash.list import List
from oslash.util import identity, compose, fmap

        # Singleton list using return
        x = unit(42)
        self.assertEqual(x.map(identity), x)

        # Empty list
        y = empty()
        self.assertEqual(y.map(identity), y)

        # Long list
        z = List.from_iterable(range(42))
        self.assertEqual(z.map(identity), z)

    def test_list_functor_law2(self):
        # fmap (f . g) x = fmap f (fmap g x)
        def f(x):
            return x + 10

        def g(x):
            return x * 10

        # Singleton list
        x = unit(42)
        self.assertEqual(x.map(compose(f, g)), x.map(g).map(f))

        # Empty list
        y = List.empty()
        self.assertEqual(y.map(compose(f, g)), y.map(g).map(f))

        # Long list
        z = List.from_iterable(range(42))

        self.assertEqual(z.map(compose(f, g)), z.map(g).map(f))