Mercurial > hg > Members > tatsuki > functionaljava-master > core
view src/main/java/fj/control/Trampoline.java @ 0:fe80c1edf1be
add getLoop
author | tatsuki |
---|---|
date | Fri, 20 Mar 2015 21:04:03 +0900 |
parents | |
children |
line wrap: on
line source
package fj.control; import fj.Bottom; import fj.F; import fj.F1Functions; import fj.F2; import fj.P1; import fj.data.Either; import fj.F2Functions; import static fj.Function.curry; import static fj.data.Either.left; import static fj.data.Either.right; /** * A Trampoline is a potentially branching computation that can be stepped through and executed in constant stack. * It represent suspendable coroutines with subroutine calls, reified as a data structure. */ public abstract class Trampoline<A> { // A Normal Trampoline is either done or suspended, and is allowed to be a subcomputation of a Codense. // This is the pointed functor part of the Trampoline monad. private static abstract class Normal<A> extends Trampoline<A> { public abstract <R> R foldNormal(final F<A, R> pure, final F<P1<Trampoline<A>>, R> k); public <B> Trampoline<B> bind(final F<A, Trampoline<B>> f) { return codense(this, f); } } // A Codense Trampoline delimits a subcomputation and tracks its current continuation. Subcomputations are only // allowed to be Normal, so all of the continuations accumulate on the right. private static final class Codense<A> extends Trampoline<A> { // The Normal subcomputation private final Normal<Object> sub; // The current continuation private final F<Object, Trampoline<A>> cont; private Codense(final Normal<Object> t, final F<Object, Trampoline<A>> k) { sub = t; cont = k; } public <R> R fold(final F<Normal<A>, R> n, final F<Codense<A>, R> gs) { return gs.f(this); } // The monadic bind constructs a new Codense whose subcomputation is still `sub`, and Kleisli-composes the // continuations. public <B> Trampoline<B> bind(final F<A, Trampoline<B>> f) { return codense(sub, new F<Object, Trampoline<B>>() { public Trampoline<B> f(final Object o) { return suspend(new P1<Trampoline<B>>() { public Trampoline<B> _1() { return cont.f(o).bind(f); } }); } }); } // The resumption of a Codense is the resumption of its subcomputation. If that computation is done, its result // gets shifted into the continuation. public Either<P1<Trampoline<A>>, A> resume() { return left(sub.resume().either(new F<P1<Trampoline<Object>>, P1<Trampoline<A>>>() { public P1<Trampoline<A>> f(final P1<Trampoline<Object>> p) { return p.map(new F<Trampoline<Object>, Trampoline<A>>() { public Trampoline<A> f(final Trampoline<Object> ot) { return ot.fold(new F<Normal<Object>, Trampoline<A>>() { public Trampoline<A> f(final Normal<Object> o) { return o.foldNormal(new F<Object, Trampoline<A>>() { public Trampoline<A> f(final Object o) { return cont.f(o); } }, new F<P1<Trampoline<Object>>, Trampoline<A>>() { public Trampoline<A> f(final P1<Trampoline<Object>> t) { return t._1().bind(cont); } } ); } }, new F<Codense<Object>, Trampoline<A>>() { public Trampoline<A> f(final Codense<Object> c) { return codense(c.sub, new F<Object, Trampoline<A>>() { public Trampoline<A> f(final Object o) { return c.cont.f(o).bind(cont); } }); } } ); } }); } }, new F<Object, P1<Trampoline<A>>>() { public P1<Trampoline<A>> f(final Object o) { return new P1<Trampoline<A>>() { public Trampoline<A> _1() { return cont.f(o); } }; } } )); } } // A suspended computation that can be resumed. private static final class Suspend<A> extends Normal<A> { private final P1<Trampoline<A>> suspension; private Suspend(final P1<Trampoline<A>> s) { suspension = s; } public <R> R foldNormal(final F<A, R> pure, final F<P1<Trampoline<A>>, R> k) { return k.f(suspension); } public <R> R fold(final F<Normal<A>, R> n, final F<Codense<A>, R> gs) { return n.f(this); } public Either<P1<Trampoline<A>>, A> resume() { return left(suspension); } } // A pure value at the leaf of a computation. private static final class Pure<A> extends Normal<A> { private final A value; private Pure(final A a) { value = a; } public <R> R foldNormal(final F<A, R> pure, final F<P1<Trampoline<A>>, R> k) { return pure.f(value); } public <R> R fold(final F<Normal<A>, R> n, final F<Codense<A>, R> gs) { return n.f(this); } public Either<P1<Trampoline<A>>, A> resume() { return right(value); } } @SuppressWarnings("unchecked") protected static <A, B> Codense<B> codense(final Normal<A> a, final F<A, Trampoline<B>> k) { return new Codense<B>((Normal<Object>) a, (F<Object, Trampoline<B>>) k); } /** * @return The first-class version of `pure`. */ public static <A> F<A, Trampoline<A>> pure() { return new F<A, Trampoline<A>>() { public Trampoline<A> f(final A a) { return pure(a); } }; } /** * Constructs a pure computation that results in the given value. * * @param a The value of the result. * @return A trampoline that results in the given value. */ public static <A> Trampoline<A> pure(final A a) { return new Pure<A>(a); } /** * Suspends the given computation in a thunk. * * @param a A trampoline suspended in a thunk. * @return A trampoline whose next step runs the given thunk. */ public static <A> Trampoline<A> suspend(final P1<Trampoline<A>> a) { return new Suspend<A>(a); } /** * @return The first-class version of `suspend`. */ public static <A> F<P1<Trampoline<A>>, Trampoline<A>> suspend_() { return new F<P1<Trampoline<A>>, Trampoline<A>>() { public Trampoline<A> f(final P1<Trampoline<A>> trampolineP1) { return suspend(trampolineP1); } }; } protected abstract <R> R fold(final F<Normal<A>, R> n, final F<Codense<A>, R> gs); /** * Binds the given continuation to the result of this trampoline. * * @param f A function that constructs a trampoline from the result of this trampoline. * @return A new trampoline that runs this trampoline, then continues with the given function. */ public abstract <B> Trampoline<B> bind(final F<A, Trampoline<B>> f); /** * Maps the given function across the result of this trampoline. * * @param f A function that gets applied to the result of this trampoline. * @return A new trampoline that runs this trampoline, then applies the given function to the result. */ public final <B> Trampoline<B> map(final F<A, B> f) { return bind(F1Functions.o(Trampoline.<B>pure(), f)); } /** * @return The first-class version of `bind`. */ public static <A, B> F<F<A, Trampoline<B>>, F<Trampoline<A>, Trampoline<B>>> bind_() { return new F<F<A, Trampoline<B>>, F<Trampoline<A>, Trampoline<B>>>() { public F<Trampoline<A>, Trampoline<B>> f(final F<A, Trampoline<B>> f) { return new F<Trampoline<A>, Trampoline<B>>() { public Trampoline<B> f(final Trampoline<A> a) { return a.bind(f); } }; } }; } /** * @return The first-class version of `map`. */ public static <A, B> F<F<A, B>, F<Trampoline<A>, Trampoline<B>>> map_() { return new F<F<A, B>, F<Trampoline<A>, Trampoline<B>>>() { public F<Trampoline<A>, Trampoline<B>> f(final F<A, B> f) { return new F<Trampoline<A>, Trampoline<B>>() { public Trampoline<B> f(final Trampoline<A> a) { return a.map(f); } }; } }; } /** * @return The first-class version of `resume`. */ public static <A> F<Trampoline<A>, Either<P1<Trampoline<A>>, A>> resume_() { return new F<Trampoline<A>, Either<P1<Trampoline<A>>, A>>() { public Either<P1<Trampoline<A>>, A> f(final Trampoline<A> aTrampoline) { return aTrampoline.resume(); } }; } /** * Runs a single step of this computation. * * @return The next step of this compuation. */ public abstract Either<P1<Trampoline<A>>, A> resume(); /** * Runs this computation all the way to the end, in constant stack. * * @return The end result of this computation. */ @SuppressWarnings("LoopStatementThatDoesntLoop") public A run() { Trampoline<A> current = this; while (true) { final Either<P1<Trampoline<A>>, A> x = current.resume(); for (final P1<Trampoline<A>> t : x.left()) { current = t._1(); } for (final A a : x.right()) { return a; } } } /** * Performs function application within a Trampoline (applicative functor pattern). * * @param lf A Trampoline resulting in the function to apply. * @return A new Trampoline after applying the given function through this Trampoline. */ public final <B> Trampoline<B> apply(final Trampoline<F<A, B>> lf) { return lf.bind(new F<F<A, B>, Trampoline<B>>() { public Trampoline<B> f(final F<A, B> f) { return map(f); } }); } /** * Binds the given function across the result of this Trampoline and the given Trampoline. * * @param lb A given Trampoline to bind the given function with. * @param f The function to combine the results of this Trampoline and the given Trampoline. * @return A new Trampoline combining the results of the two trampolines with the given function. */ public final <B, C> Trampoline<C> bind(final Trampoline<B> lb, final F<A, F<B, C>> f) { return lb.apply(map(f)); } /** * Promotes the given function of arity-2 to a function on Trampolines. * * @param f The function to promote to a function on Trampolines. * @return The given function, promoted to operate on Trampolines. */ public static <A, B, C> F<Trampoline<A>, F<Trampoline<B>, Trampoline<C>>> liftM2(final F<A, F<B, C>> f) { return curry(new F2<Trampoline<A>, Trampoline<B>, Trampoline<C>>() { public Trampoline<C> f(final Trampoline<A> as, final Trampoline<B> bs) { return as.bind(bs, f); } }); } /** * Combines two trampolines so they run cooperatively. The results are combined with the given function. * * @param b Another trampoline to combine with this trampoline. * @param f A function to combine the results of the two trampolines. * @return A new trampoline that runs this trampoline and the given trampoline simultaneously. */ @SuppressWarnings("LoopStatementThatDoesntLoop") public <B, C> Trampoline<C> zipWith(final Trampoline<B> b, final F2<A, B, C> f) { final Either<P1<Trampoline<A>>, A> ea = resume(); final Either<P1<Trampoline<B>>, B> eb = b.resume(); for (final P1<Trampoline<A>> x : ea.left()) { for (final P1<Trampoline<B>> y : eb.left()) { return suspend(x.bind(y, F2Functions.curry(new F2<Trampoline<A>, Trampoline<B>, Trampoline<C>>() { public Trampoline<C> f(final Trampoline<A> ta, final Trampoline<B> tb) { return suspend(new P1<Trampoline<C>>() { public Trampoline<C> _1() { return ta.zipWith(tb, f); } }); } }))); } for (final B y : eb.right()) { return suspend(x.map(new F<Trampoline<A>, Trampoline<C>>() { public Trampoline<C> f(final Trampoline<A> ta) { return ta.map(F2Functions.f(F2Functions.flip(f), y)); } })); } } for (final A x : ea.right()) { for (final B y : eb.right()) { return suspend(new P1<Trampoline<C>>() { public Trampoline<C> _1() { return pure(f.f(x, y)); } }); } for (final P1<Trampoline<B>> y : eb.left()) { return suspend(y.map(liftM2(F2Functions.curry(f)).f(pure(x)))); } } throw Bottom.error("Match error: Trampoline is neither done nor suspended."); } }