package me.elrod.pureio;

import java.util.function.Function;

/**
 * <code>Free (F =&gt;</code> {@link ConsoleFileCoproduct}<code>)</code>.
 */
public abstract class ConsoleFileFree<A> {
    private ConsoleFileFree() {
    }

    // Functor
    public <B> ConsoleFileFree<B> map(Function<A, B> f) {
        return cata(
            a -> ConsoleFileFree.pure(f.apply(a)),
            a -> ConsoleFileFree.free(a.map(k -> k.map(f))));
    }

    // Free monad
    public <B> ConsoleFileFree<B> flatMap(Function<A, ConsoleFileFree<B>> f) {
        return cata(
            f,
            a -> ConsoleFileFree.free(a.map(k -> k.flatMap(f))));
    }

    public abstract <B> B cata(
        Function<A, B> pure,
        Function<ConsoleFileCoproduct<ConsoleFileFree<A>>, B> free);

    //----------------------------------------------------------------------

    final static class Pure<A> extends ConsoleFileFree<A> {
        private A a;

        public Pure(A a) {
            this.a = a;
        }

        public <B> B cata(
            Function<A, B> pure,
            Function<ConsoleFileCoproduct<ConsoleFileFree<A>>, B> free) {
            return pure.apply(a);
        }
    }


    public static <A> ConsoleFileFree<A> pure(A a) {
        return new Pure<A>(a);
    }

    //----------------------------------------------------------------------

    final static class Free<A> extends ConsoleFileFree<A> {
        private ConsoleFileCoproduct<ConsoleFileFree<A>> a;

        public Free(ConsoleFileCoproduct<ConsoleFileFree<A>> a) {
            this.a = a;
        }

        public <B> B cata(
            Function<A, B> pure,
            Function<ConsoleFileCoproduct<ConsoleFileFree<A>>, B> free) {
            return free.apply(a);
        }
    }

    public static <A> ConsoleFileFree<A> free(ConsoleFileCoproduct<ConsoleFileFree<A>> a) {
        return new Free<A>(a);
    }
}

@@ -19,16 +19,54 @@
 ureio;%0A%0A
+import java.util.function.BiFunction;%0A
 import j
@@ -1921,11 +1921,1076 @@
 );%0A    %7D
+%0A%0A    /**%0A     * Evaluate each action in the sequence from left to right and collect the%0A     * results.%0A     *%0A     * %3Cbr%3E%0A     * %3Ccode%3E%0A     * sequence :: Monad m =&gt; %5Bm a%5D -&gt; m %5Ba%5D%0A     * sequence ms = foldr k (return %5B%5D) ms%0A     *      where%0A     *        k m m' = do %7B x &lt;- m; xs &lt;- m'; return (x:xs) %7D%0A     * %3C/code%3E%0A     */%0A    public static %3CA%3E ConsoleFileFree%3CLinkedList%3CA%3E%3E sequence(LinkedList%3CConsoleFileFree%3CA%3E%3E ll) %7B%0A        return ll.foldRight(%0A            // k :: Monad m =%3E m a -%3E m %5Ba%5D -%3E m %5Ba%5D%0A            (a,b) -%3E a.flatMap(aPrime -%3E b.flatMap(bPrime -%3E ConsoleFileFree.pure(bPrime.cons(aPrime)))),%0A            ConsoleFileFree.pure(new LinkedList.Nil%3CA%3E()));%0A    %7D%0A%0A    /**%0A     * Same as %3Ccode%3Esequence %E2%88%98 map%3C/code%3E.%0A     *%0A     * %3Cbr%3E%0A     * %3Ccode%3E%0A     * mapM :: Monad m =&gt; (a -&gt; m b) -&gt; %5Ba%5D -&gt; m %5Bb%5D%0A     * mapM f as = sequence (map f as)%0A     * %3C/code%3E%0A     */%0A    public static %3CA, B%3E ConsoleFileFree%3CLinkedList%3CB%3E%3E mapM(Function%3CA, ConsoleFileFree%3CB%3E%3E f, LinkedList%3CA%3E ll) %7B%0A        return sequence(ll.map(f));%0A    %7D
 %0A%7D%0A

add sequence and mapM to ConsoleFileFree