package haskell.prelude;

import haskell.lang.Class;

/**
 * class Monad m
 */
public interface Monad<A, M extends Monad<A, M>>
        extends Class<M> {

    abstract class Support
            <A, B, MA extends Monad<A, MA>, MB extends Monad<B, MB>> {

        // Minimal complete definition:
        //     (>>=), return

        /**
         * (>>=) :: m a -> (a -> m b) -> m b
         */
        public final Operator<MA, Function<A, MB>, MB> bind() {
            return new Operator<MA, Function<A, MB>, MB>(">>=") {
                @Override
                public MB apply(final MA m, final Function<A, MB> k) {
                    return Monad.Support.this.bind(m, k);
                }
            };
        }

        public abstract MB bind(MA m, Function<A, MB> k);

        /**
         * (>>) :: m a -> m b -> m b
         */
        public final Operator<MA, MB, MB> bind_() {
            return new Operator<MA, MB, MB>(">>") {
                @Override
                public MB apply(final MA m, final MB k) {
                    return bind_(m, k);
                }
            };
        }

        /**
         * m >> k = m >>= \_ -> k
         */
        public MB bind_(final MA m, final MB k) {
            return bind(m, new Function<A, MB>() {
                @Override
                public MB apply(final A x) {
                    return k;
                }
            });
        }

        /**
         * return :: a -> m a
         */
        public final Function<A, MA> return_() {
            return new Function<A, MA>("return") {
                @Override
                public MA apply(final A x) {
                    return return_(x);
                }
            };
        }

        public abstract MA return_(A x);

        /**
         * fail :: String -> m a
         */
        public final Function<List<Char>, MA> fail() {
            return new Function<List<Char>, MA>() {
                @Override
                public MA apply(final List<Char> x) {
                    return fail(x);
                }
            };
        }

        /**
         * fail s = error s
         */
        public MA fail(List<Char> s) {
            return Function.error(s);
        }

        /**
         * sequence :: Monad m => [m a] -> m [a]
         */

        /**
         * sequence_ :: Monad m => [m a] -> m ()
         */

        /**
         * mapM :: Monad m => (a -> m b) -> [a] -> m [b]
         */

        /**
         * mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
         */

        /**
         * (=<<) :: Monad m => (a -> m b) -> m a -> m b
         */

    }

}
