<NME> writer.py
<BEF> from .functor import Functor
from .monad import Monad


class Writer(Monad, Functor):

    """The writer monad."""

    def __init__(self, value, log):
        """Initialize a new writer.

        :param value Any: Value to

    def __init__(self, value: TSource, log: TLog) -> None:

        self._get_value = lambda: (value, log)

    def fmap(self, func) -> "Writer":
        """Map a function func over the Writer value.

        Haskell:
    def map(self, func: Callable[[Tuple[TSource, TLog]], Tuple[TResult, TLog]]) -> 'Writer[TResult, TLog]':
        """Map a function func over the Writer value.

        Haskell:
        fmap f m = Writer $ let (a, w) = runWriter m in (f a, w)
        value, log = self.run_writer()
        return Writer(func(value), log)

    def bind(self, func) -> "Writer":
        """Flat is better than nested.

        Haskell:

    def bind(self, func: Callable[[TSource], 'Writer[TResult, TLog]']) -> 'Writer[TResult, TLog]':
        """Flat is better than nested.

        Haskell:
        (Writer (x, v)) >>= f = let
            (Writer (y, v')) = f x in Writer (y, v `append` v')
        """
        a, w = self.run()
        b, w_ = func(a).run_writer()
        return Writer(b, w + w_)

    def __eq__(self, other):
        return self.run_writer() == other.run_writer()

    def __str__(self):
        return "%s :: %s" % self.run_writer()

    def __repr__(self):
        return str(self)

    @classmethod
    def return_(cls, value, monoid=str):
        # Get default value for empty log monoid
        log = monoid.mempty() if hasattr(monoid, "mempty") else monoid()
        return cls(value, log)

    def run_writer(self):
        """Convert Writer to s simple tuple."""
        return self._get_value()

    @staticmethod
    def apply_log(a: tuple, func) -> tuple:
        """Apply a function to a value with a log.

        Helper function to apply a function to a value with a log tuple.

        Helper function to apply a function to a value with a log tuple.
        """
        value, log = a
        new, entry = func(value)
        return new, log + entry

    @classmethod
    def create(cls, class_name: str, monoid_type=Union[Monoid, str]):
        """Create Writer subclass using specified monoid type. lets us
        create a Writer that uses a different monoid than str for the
        log.

        Usage:
            StringWriter = Writer.create("StringWriter", str)
            IntWriter = Writer.create("IntWriter", int)
            ...
        """

        def unit(cls, value):
            if hasattr(monoid_type, "empty"):
                log = monoid_type.empty()
            else:
                log = monoid_type()

            return cls(value, log)

        return type(class_name, (Writer, ), dict(unit=classmethod(unit)))

    def __eq__(self, other) -> bool:
        return self.run() == other.run()

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

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


class MonadWriter(Writer[Any, TLog]):

    @classmethod
    def tell(cls, log: TLog) -> 'MonadWriter':
        return cls(None, log)


StringWriter = Writer.create("StringWriter", str)


assert(isinstance(Writer, Functor))
assert(isinstance(Writer, Monad))

<MSG> Added type annotations

<DFF> @@ -1,12 +1,13 @@
 from .functor import Functor
 from .monad import Monad
+from .monoid import Monoid
 
 
 class Writer(Monad, Functor):
 
     """The writer monad."""
 
-    def __init__(self, value, log):
+    def __init__(self, value: "Any", log: Monoid):
         """Initialize a new writer.
 
         :param value Any: Value to
@@ -15,7 +16,7 @@ class Writer(Monad, Functor):
 
         self._get_value = lambda: (value, log)
 
-    def fmap(self, func) -> "Writer":
+    def fmap(self, func: "Callable[[Any], Any]") -> "Writer":
         """Map a function func over the Writer value.
 
         Haskell:
@@ -27,7 +28,7 @@ class Writer(Monad, Functor):
         value, log = self.run_writer()
         return Writer(func(value), log)
 
-    def bind(self, func) -> "Writer":
+    def bind(self, func: "Callable[[Any], Writer]") -> "Writer":
         """Flat is better than nested.
 
         Haskell:
@@ -43,27 +44,27 @@ class Writer(Monad, Functor):
         b, w_ = func(a).run_writer()
         return Writer(b, w + w_)
 
-    def __eq__(self, other):
+    def __eq__(self, other: "Writer") -> bool:
         return self.run_writer() == other.run_writer()
 
-    def __str__(self):
+    def __str__(self) -> str:
         return "%s :: %s" % self.run_writer()
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return str(self)
 
     @classmethod
-    def return_(cls, value, monoid=str):
+    def return_(cls, value: "Any", monoid=str) -> "Writer":
         # Get default value for empty log monoid
         log = monoid.mempty() if hasattr(monoid, "mempty") else monoid()
         return cls(value, log)
 
-    def run_writer(self):
+    def run_writer(self) -> tuple:
         """Convert Writer to s simple tuple."""
         return self._get_value()
 
     @staticmethod
-    def apply_log(a: tuple, func) -> tuple:
+    def apply_log(a: tuple, func: "Callabe[[Any], Tuple[Any, Monoid]]") -> tuple:
         """Apply a function to a value with a log.
 
         Helper function to apply a function to a value with a log tuple.

Added type annotations