# Path: oslash/typing/functor.py
# class Functor(Protocol[TSource]):
#     """The Functor class is used for types that can be mapped over.
# 
#     Instances of Functor should satisfy the following laws:
# 
#     Haskell:
#     fmap id  ==  id
#     fmap (f . g)  ==  fmap f . fmap g
# 
#     Python:
#     x.map(id) == id(x)
#     x.map(compose(f, g)) == x.map(g).map(f)
# 
#     The instances of Functor for lists, Maybe and IO satisfy these laws.
#     """
# 
#     @abstractmethod
#     def map(self, fn: Callable[[TSource], TResult]) -> 'Functor[TResult]':
#         """Map a function over wrapped values.
# 
#         Map knows how to apply functions to values that are wrapped in
#         a context.
#         """
#         raise NotImplementedError
# 
#     # 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)
# 
# Path: oslash/typing/monad.py
# class Monad(Protocol[TSource]):
#     """Monad protocol"""
# 
#     @abstractmethod
#     def bind(self, fn: Callable[[TSource], 'Monad[TResult]']) -> 'Monad[TResult]':
#         """Monad bind method.
# 
#         Python: bind(self: Monad[A], func: Callable[[A], Monad[B]]) -> Monad[B]
#         Haskell: (>>=) :: m a -> (a -> m b) -> m b
# 
#         This is the mother of all methods. It's hard to describe what it
#         does, because it can be used for pretty much anything:
# 
#         * Transformation, for projecting Monadic values and functions.
#         * Composition, for composing monadic functions.
#         * Chaining, for chaining of functions as a monadic value.
#         * Combining, for combining monadic values.
#         * Sequencing, of Monadic functions.
#         * Flattening, of nested Monadic values.
#         * Variable substitution, assign values to variables.
# 
#         The Monad doesn’t specify what is happening, only that whatever
#         is happening satisfies the laws of associativity and identity.
# 
#         Returns a new Monad.
#         """
#         raise NotImplementedError
# 
#     @classmethod
#     @abstractmethod
#     def unit(value: TSource) -> 'Monad[TSource]':
#         """Wrap a value in a default context.
# 
#         Haskell: return :: a -> m a .
# 
#         Inject a value into the monadic type. Since return is a reserved
#         word in Python, we align with Scala and use the name unit
#         instead.
#         """
#         raise NotImplementedError
# 
#     #def lift(self, func: Callable) -> Monad[B]:
#     #    """Map function over monadic value.
#     #
#     #    Takes a function and a monadic value and maps the function over the
#     #    monadic value
#     #
#     #    Haskell: liftM :: (Monad m) => (a -> b) -> m a -> m b
#     #
#     #    This is really the same function as Functor.fmap, but is instead
#     #    implemented using bind, and does not rely on us inheriting from
#     #    Functor.
#     #    """
#     #
#     #    return self.bind(lambda x: Monad.unit(func(x)))
# 
# 
#     #def join(self):
#     #    """join :: Monad m => m (m a) -> m a
#     #
#     #    The join function is the conventional monad join operator. It is
#     #    used to remove one level of monadic structure, projecting its
#     #    bound argument into the outer level."""
# 
#     #    return self.bind(identity)
# 
# Path: oslash/util/basic.py
# def indent(level, size=2):


from abc import abstractmethod
from typing import Any, Callable, Generic, TypeVar, Tuple
from .typing import Functor
from .typing import Monad
from .util import indent as ind, Unit
    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)


# Path: oslash/typing/functor.py
# class Functor(Protocol[TSource]):
#     """The Functor class is used for types that can be mapped over.
# 
#     Instances of Functor should satisfy the following laws:
# 
#     Haskell:
#     fmap id  ==  id
#     fmap (f . g)  ==  fmap f . fmap g
# 
#     Python:
#     x.map(id) == id(x)
#     x.map(compose(f, g)) == x.map(g).map(f)
# 
#     The instances of Functor for lists, Maybe and IO satisfy these laws.
#     """
# 
#     @abstractmethod
#     def map(self, fn: Callable[[TSource], TResult]) -> 'Functor[TResult]':
#         """Map a function over wrapped values.
# 
#         Map knows how to apply functions to values that are wrapped in
#         a context.
#         """
#         raise NotImplementedError
# 
#     # 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)
# 
# Path: oslash/typing/monad.py
# class Monad(Protocol[TSource]):
#     """Monad protocol"""
# 
#     @abstractmethod
#     def bind(self, fn: Callable[[TSource], 'Monad[TResult]']) -> 'Monad[TResult]':
#         """Monad bind method.
# 
#         Python: bind(self: Monad[A], func: Callable[[A], Monad[B]]) -> Monad[B]
#         Haskell: (>>=) :: m a -> (a -> m b) -> m b
# 
#         This is the mother of all methods. It's hard to describe what it
#         does, because it can be used for pretty much anything:
# 
#         * Transformation, for projecting Monadic values and functions.
#         * Composition, for composing monadic functions.
#         * Chaining, for chaining of functions as a monadic value.
#         * Combining, for combining monadic values.
#         * Sequencing, of Monadic functions.
#         * Flattening, of nested Monadic values.
#         * Variable substitution, assign values to variables.
# 
#         The Monad doesn’t specify what is happening, only that whatever
#         is happening satisfies the laws of associativity and identity.
# 
#         Returns a new Monad.
#         """
#         raise NotImplementedError
# 
#     @classmethod
#     @abstractmethod
#     def unit(value: TSource) -> 'Monad[TSource]':
#         """Wrap a value in a default context.
# 
#         Haskell: return :: a -> m a .
# 
#         Inject a value into the monadic type. Since return is a reserved
#         word in Python, we align with Scala and use the name unit
#         instead.
#         """
#         raise NotImplementedError
# 
#     #def lift(self, func: Callable) -> Monad[B]:
#     #    """Map function over monadic value.
#     #
#     #    Takes a function and a monadic value and maps the function over the
#     #    monadic value
#     #
#     #    Haskell: liftM :: (Monad m) => (a -> b) -> m a -> m b
#     #
#     #    This is really the same function as Functor.fmap, but is instead
#     #    implemented using bind, and does not rely on us inheriting from
#     #    Functor.
#     #    """
#     #
#     #    return self.bind(lambda x: Monad.unit(func(x)))
# 
# 
#     #def join(self):
#     #    """join :: Monad m => m (m a) -> m a
#     #
#     #    The join function is the conventional monad join operator. It is
#     #    used to remove one level of monadic structure, projecting its
#     #    bound argument into the outer level."""
# 
#     #    return self.bind(identity)
# 
# Path: oslash/util/basic.py
# def indent(level, size=2):


# Path: oslash/ioaction.py
from abc import abstractmethod
from typing import Any, Callable, Generic, TypeVar, Tuple
from .typing import Functor
from .typing import Monad
from .util import indent as ind, Unit

    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)