<NME> ioaction.py
<BEF> from functools import partial

from .applicative import Applicative
TSource = TypeVar("TSource")
TResult = TypeVar("TResult")



class IOAction(Monad, Monoid, Applicative, Functor, list):

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

    """

    @classmethod
    def unit(cls, value: TSource):
        return Return(value)

    @abstractmethod
    def bind(self, func: Callable[[TSource], "IO[TResult]"]) -> "IO[TResult]":
        """IO a -> (a -> IO b) -> IO b."""

        raise NotImplementedError

    @abstractmethod
    def map(self, func: Callable[[TSource], TResult]) -> "IO[TResult]":

class Put(IOAction):
    def __init__(self, text: str, action: IOAction):
        self._get_value = lambda: (text, action)

    def bind(self, func) -> "IOAction":
    def __or__(self, func):
        """Use | as operator for bind.

        Provide the | operator instead of the Haskell >>= operator
        """
        return self.bind(func)

    def __rshift__(self, next: 'IO[TResult]') -> 'IO[TResult]':
        """The "Then" operator.
        Sequentially compose two monadic actions, discarding any value
        produced by the first, like sequencing operators (such as the
        semicolon) in imperative languages.
        Haskell: (>>) :: m a -> m b -> m b
        """
        return self.bind(lambda _: next)

    def __call__(self, world: int = 0) -> Any:
        """Run io action."""
        return self.run(world)

    @abstractmethod
    def __str__(self, m: int = 0, n: int = 0) -> str:
        raise NotImplementedError

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


class Return(IO[TSource]):
    def __init__(self, value: TSource) -> None:
        """Create IO Action."""

        self._value = value

    def map(self, func: Callable[[TSource], TResult]) -> "IO[TResult]":
        return Return(func(self._value))

    def bind(self, func: Callable[[TSource], "IO[TResult]"]) -> "IO[TResult]":
        """IO a -> (a -> IO b) -> IO b."""

        return func(self._value)

    def run(self, world: int) -> TSource:
        """Run IO action."""
        return self._value

    def __str__(self, m: int = 0, n: int = 0) -> str:
        a = self._value
        return f"{ind(m)}Return {a}"


class Put(IO[TSource]):
    """The Put action.

    A container holding a string to be printed to stdout, followed by
    another IO Action.
    """

    def __init__(self, text: str, io: IO) -> None:
        self._value = text, io

    def bind(self, func: Callable[[TSource], IO[TResult]]) -> 'IO[TResult]':
        """IO a -> (a -> IO b) -> IO b"""

        text, io = self._value
        return Put(text, io.bind(func))

    def map(self, func: Callable[[TSource], TResult]) -> "IO[TResult]":
        # Put s (fmap f io)
        assert self._value is not None
        text, action = self._value
        return Put(text, action.map(func))

    def run(self, world: int) -> TSource:
        """Run IO action"""

        assert self._value is not None
        text, action = self._value
        new_world = pure_print(world, text)
        return action(world=new_world)

    def __call__(self, world: int = 0) -> TSource:
        return self.run(world)

    def __str__(self, m: int = 0, n: int = 0) -> str:
        s, io = self._value
        a = io.__str__(m + 1, n)
        return '%sPut ("%s",\n%s\n%s)' % (ind(m), s, a, ind(m))


class Get(IO[TSource]):
    """A container holding a function from string -> IO[TSource], which can
    be applied to whatever string is read from stdin.
    """

    def __init__(self, fn: Callable[[str], IO[TSource]]) -> None:
        self._fn = fn

    def bind(self, func: Callable[[TSource], IO[TResult]]) -> IO[TResult]:
        """IO a -> (a -> IO b) -> IO b"""

        g = self._fn
        return Get(lambda text: g(text).bind(func))

    def map(self, func: Callable[[TSource], TResult]) -> IO[TResult]:
        # Get (\s -> fmap f (g s))
        g = self._fn
        return Get(lambda s: g(s).map(func))

    def run(self, world: int) -> TSource:
        """Run IO Action"""

        func = self._fn
        new_world, text = pure_input(world)
        action = func(text)
        return action(world=new_world)

    def __call__(self, world: int = 0) -> TSource:
        return self.run(world)

    def __str__(self, m: int = 0, n: int = 0) -> str:
        g = self._fn
        i = "x%s" % n
        a = g(i).__str__(m + 1, n + 1)
        return "%sGet (%s => \n%s\n%s)" % (ind(m), i, a, ind(m))


class ReadFile(IO[str]):
    """A container holding a filename and a function from string -> IO[str],
    which can be applied to whatever string is read from the file.
    """

    def __init__(self, filename: str, func: Callable[[str], IO]) -> None:
        self.open_func = open
        self._value = filename, func

    def bind(self, func: Callable[[Any], IO]) -> IO:
        """IO a -> (a -> IO b) -> IO b"""

        filename, g = self._value
        return ReadFile(filename, lambda s: g(s).bind(func))

    def map(self, func: Callable[[Any], Any]) -> IO:
        # Get (\s -> fmap f (g s))
        filename, g = self._value
        return Get(lambda s: g(s).map(func))

    def run(self, world: int) -> str:
        """Run IO Action"""

        filename, func = self._value
        f = self.open_func(filename)
        action = func(f.read())
        return action(world=world + 1)

    def __call__(self, world: int = 0) -> str:
        return self.run(world)

    def __str__(self, m: int = 0, n: int = 0) -> str:
        filename, g = self._value
        i = "x%s" % n
        a = g(i).__str__(m + 2, n + 1)
        return '%sReadFile ("%s",%s => \n%s\n%s)' % (ind(m), filename, i, a, ind(m))


def get_line() -> IO[str]:
    return Get(Return)


def put_line(text: str) -> IO:
    return Put(text, Return(Unit))


def read_file(filename: str) -> IO:
    return ReadFile(filename, Return)


def pure_print(world: int, text: str) -> int:
    print(text)  # Impure. NOTE: If you see this line you need to wash your hands
    return world + 1


def pure_input(world: int) -> Tuple[int, str]:
    text = input()  # Impure. NOTE: If you see this line you need to wash your hands
    return (world + 1, text)


assert isinstance(IO, Functor)
assert isinstance(IO, Monad)

assert isinstance(Put, Functor)
assert isinstance(Put, Monad)

assert isinstance(Get, Functor)
assert isinstance(Get, Monad)

assert isinstance(ReadFile, Functor)
assert isinstance(ReadFile, Monad)

<MSG> Cleanup

<DFF> @@ -1,3 +1,9 @@
+"""Implementation of IOActions - The Gods Must Be Crazy
+
+Many thanks to Chris Taylor and his blog post "IO Is Pure",
+http://chris-taylor.github.io/blog/2013/02/09/io-is-not-a-side-effect/
+"""
+
 from functools import partial
 
 from .applicative import Applicative
@@ -8,7 +14,7 @@ from .monad import Monad
 
 class IOAction(Monad, Monoid, Applicative, Functor, list):
 
-    def __init__(self, value):
+    def __init__(self, value=None):
         super().__init__()
         self._get_value = lambda: value
 
@@ -29,6 +35,7 @@ class IOAction(Monad, Monoid, Applicative, Functor, list):
 
 class Put(IOAction):
     def __init__(self, text: str, action: IOAction):
+        super().__init__()
         self._get_value = lambda: (text, action)
 
     def bind(self, func) -> "IOAction":

Cleanup