<NME> maybe.py
<BEF> from abc import ABCMeta, abstractmethod
from functools import partial

from .abc import Applicative
from .abc import Functor
from .abc import Monoid
from .typing import Monad

TSource = TypeVar("TSource")
TResult = TypeVar("TResult")


class Maybe(Generic[TSource]):
    """Encapsulates an optional value.

    The Maybe type encapsulates an optional value. A value of type
    Maybe a either contains a value of (represented as Just a), or it is
    empty (represented as Nothing). Using Maybe is a good way to deal
    """

    @abstractmethod
    def bind(self, func) -> "Maybe":
        return NotImplemented

    @abstractmethod
    def fmap(self, _) -> "Maybe":
        return NotImplemented

    @abstractmethod
    def apply(self, something) -> "Maybe":
        return NotImplemented

    @classmethod

        return Nothing()

    @abstractmethod
    def mappend(self, other) -> "Maybe":
        return NotImplemented

    @abstractmethod
    def __eq__(self, other) -> bool:
        return NotImplemented

    def __repr__(self) -> str:
        raise NotImplementedError

    @abstractmethod
    def bind(self, fn: Callable[[TSource], "Maybe[TResult]"]) -> "Maybe[TResult]":
        raise NotImplementedError

    @classmethod
    def concat(cls, xs):
        """mconcat :: [m] -> m

    def __init__(self, value):
        self._get_value = lambda: value

    def fmap(self, mapper) -> Maybe:
        # fmap f (Just x) = Just (f x)

        value = self._get_value()
            return a + b

        return reduce(reducer, xs, cls.empty())

    def __rmod__(self, fn):
        """Infix version of map.

        Haskell: <$>

        Example:
        >>> (lambda x: x+2) % Just(40)
        42

        Returns a new Functor.
        """
        return self.map(fn)


class Just(Maybe[TSource]):
    """A Maybe that contains a value.

    Represents a Maybe that contains a value (represented as Just a).
        # Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
        return Just(value.mappend(other_value))

    def bind(self, func) -> Maybe:
        """Just x >>= f = f x"""

        value = self._get_value()

    def __add__(self, other: Maybe[TSource]) -> Maybe[TSource]:
        # m `append` Nothing = m
        if isinstance(other, Nothing):
            return self

        # Just m1 `append` Just m2 = Just (m1 `append` m2)
        return other.map(
            lambda other_value: cast(Any, self._value) + other_value if hasattr(self._value, "__add__") else Nothing()
        )

    # Functor Section
    # ===============

    the value of Nothing).
    """

    def fmap(self, _) -> Maybe:
        return Nothing()

    def apply(self, _) -> Maybe:
        return Nothing()

    def mappend(self, other: Maybe) -> Maybe:
        return other

    def bind(self, func) -> Maybe:
        """Nothing >>= f = Nothing

        Nothing in, Nothing out.
            try:
                return self._value(other_value)
            except TypeError:
                return partial(self._value, other_value)

        return something.map(mapper)

    # Monad Section
    # =============

    @classmethod
    def unit(cls, value: TSource) -> Maybe[TSource]:
        return Just(value)

    def bind(self, func: Callable[[TSource], Maybe[TResult]]) -> Maybe[TResult]:
        """Just x >>= f = f x."""

        value = self._value
        return func(value)

    # Utilities Section
    # =================

    def is_just(self) -> bool:
        return True

    def is_nothing(self) -> bool:
        return False

    # Operator Overloads Section
    # ==========================

    def __bool__(self) -> bool:
        """Convert Just to bool."""
        return bool(self._value)

    def __eq__(self, other) -> bool:
        """Return self == other."""

        if isinstance(other, Nothing):
            return False

        return bool(other.map(lambda other_value: other_value == self._value))

    def __str__(self) -> str:
        return "Just %s" % self._value

    def __repr__(self) -> str:
        return str(self)


class Nothing(Maybe[TSource]):

    """Represents an empty Maybe.

    Represents an empty Maybe that holds nothing (in which case it has
    the value of Nothing).
    """

    # Monoid Section
    # ==============

    def __add__(self, other: Maybe) -> Maybe:
        # m `append` Nothing = m
        return other

    # Functor Section
    # ===============

    def map(self, mapper: Callable[[TSource], TResult]) -> Maybe[TResult]:
        return Nothing()

    # Applicative Section
    # ===================

    @classmethod
    def pure(cls, value: Callable[[TSource], TResult]) -> Maybe[Callable[[TSource], TResult]]:
        return Nothing()

    def apply(self: "Nothing[Callable[[TSource], TResult]]", something: Maybe[TSource]) -> Maybe[TResult]:
        return Nothing()

    # Monad Section
    # =============

    @classmethod
    def unit(cls, value: TSource) -> Maybe[TSource]:
        return cls()

    def bind(self, func: Callable[[TSource], Maybe[TResult]]) -> Maybe[TResult]:
        """Nothing >>= f = Nothing

        Nothing in, Nothing out.
        """

        return Nothing()

    # Utilities Section
    # =================

    def is_pure(self) -> bool:
        return False

    def is_nothing(self) -> bool:
        return True

    # Operator Overloads Section
    # ==========================

    def __eq__(self, other) -> bool:
        return isinstance(other, Nothing)

    def __str__(self) -> str:
        return "Nothing"

    def __repr__(self) -> str:
        return str(self)


assert issubclass(Just, Maybe)
assert issubclass(Nothing, Maybe)

assert isinstance(Maybe, Monoid)
assert isinstance(Maybe, Functor)
assert isinstance(Maybe, Applicative)
assert isinstance(Maybe, Monad)

assert isinstance(Just, Monoid)
assert isinstance(Just, Functor)
assert isinstance(Just, Applicative)
assert isinstance(Just, Monad)

assert isinstance(Nothing, Monoid)
assert isinstance(Nothing, Functor)
assert isinstance(Nothing, Applicative)
assert isinstance(Nothing, Monad)

<MSG> Fixed docs and typing

<DFF> @@ -1,6 +1,8 @@
 from abc import ABCMeta, abstractmethod
 from functools import partial
 
+from typing import Any, Callable
+
 from .abc import Applicative
 from .abc import Functor
 from .abc import Monoid
@@ -19,15 +21,15 @@ class Maybe(Monad, Monoid, Applicative, Functor, metaclass=ABCMeta):
     """
 
     @abstractmethod
-    def bind(self, func) -> "Maybe":
+    def bind(self, func: Callable[[Any], "Maybe"]) -> "Maybe":
         return NotImplemented
 
     @abstractmethod
-    def fmap(self, _) -> "Maybe":
+    def fmap(self, mapper: Callable[[Any], Any]) -> "Maybe":
         return NotImplemented
 
     @abstractmethod
-    def apply(self, something) -> "Maybe":
+    def apply(self, something: "Maybe") -> "Maybe":
         return NotImplemented
 
     @classmethod
@@ -35,11 +37,11 @@ class Maybe(Monad, Monoid, Applicative, Functor, metaclass=ABCMeta):
         return Nothing()
 
     @abstractmethod
-    def mappend(self, other) -> "Maybe":
+    def mappend(self, other: "Maybe") -> "Maybe":
         return NotImplemented
 
     @abstractmethod
-    def __eq__(self, other) -> bool:
+    def __eq__(self, other: "Maybe") -> bool:
         return NotImplemented
 
     def __repr__(self) -> str:
@@ -56,7 +58,7 @@ class Just(Maybe):
     def __init__(self, value):
         self._get_value = lambda: value
 
-    def fmap(self, mapper) -> Maybe:
+    def fmap(self, mapper: Callable[[Any], Any]) -> "Just":
         # fmap f (Just x) = Just (f x)
 
         value = self._get_value()
@@ -85,7 +87,7 @@ class Just(Maybe):
         # Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
         return Just(value.mappend(other_value))
 
-    def bind(self, func) -> Maybe:
+    def bind(self, func: Callable[[Any], Maybe]) -> Maybe:
         """Just x >>= f = f x"""
 
         value = self._get_value()
@@ -106,16 +108,16 @@ class Nothing(Maybe):
     the value of Nothing).
     """
 
-    def fmap(self, _) -> Maybe:
+    def fmap(self, mapper: Callable[[Any], Any]) -> Maybe:
         return Nothing()
 
-    def apply(self, _) -> Maybe:
+    def apply(self, other: Maybe) -> Maybe:
         return Nothing()
 
     def mappend(self, other: Maybe) -> Maybe:
         return other
 
-    def bind(self, func) -> Maybe:
+    def bind(self, func: Callable[[Any], Maybe]) -> Maybe:
         """Nothing >>= f = Nothing
 
         Nothing in, Nothing out.

Fixed docs and typing