# 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 collections import namedtuple
from .typing import Monad
from .util import Unit
            for k in names_to_clear:
                delattr(self, k)
            self.names = freevars.copy()

    # stuff used inside the eval
    e = env()

    def begin(*exprs):  # args eagerly evaluated by Python
        # begin(e1, e2, ..., en):
        #   perform side effects e1, e2, ..., e[n-1], return the value of en.
        return exprs[-1]

    allcode = ""
    names = set()  # names seen so far (working line by line, so textually!)
    bodys = []
    begin_is_open = False
    for j, item in enumerate(lines):
        is_first = j == 0
        is_last = j == len(lines) - 1

        if isinstance(item, MonadicLet):
            name, body = item
        else:
            name, body = None, item
        bodys.append(body)

        freevars = names.copy()  # names from the surrounding scopes
        if name:
            names.add(name)


# 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/do.py
from collections import namedtuple
from .typing import Monad
from .util import Unit

            for k in names_to_clear:
                delattr(self, k)
            self.names = freevars.copy()

    # stuff used inside the eval
    e = env()

    def begin(*exprs):  # args eagerly evaluated by Python
        # begin(e1, e2, ..., en):
        #   perform side effects e1, e2, ..., e[n-1], return the value of en.
        return exprs[-1]

    allcode = ""
    names = set()  # names seen so far (working line by line, so textually!)
    bodys = []
    begin_is_open = False
    for j, item in enumerate(lines):
        is_first = j == 0
        is_last = j == len(lines) - 1

        if isinstance(item, MonadicLet):
            name, body = item
        else:
            name, body = None, item
        bodys.append(body)

        freevars = names.copy()  # names from the surrounding scopes
        if name:
            names.add(name)


        if isinstance(body, Monad):  # doesn't need the environment