fj.control.parallel.Promise Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
package fj.control.parallel;
import fj.*;
import static fj.P.p;
import static fj.Function.curry;
import static fj.Function.identity;
import static fj.control.parallel.Actor.actor;
import static fj.control.parallel.Callables.normalise;
import static fj.control.parallel.Actor.queueActor;
import fj.data.Either;
import fj.data.List;
import fj.data.Option;
import static fj.data.Option.none;
import static fj.data.Option.some;
import fj.data.Stream;
import fj.function.Effect1;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Represents a non-blocking future value. Products, functions, and actors, given to the methods on this class,
* are executed concurrently, and the Promise serves as a handle on the result of the computation. Provides monadic
* operations so that future computations can be combined
*
* Author: Runar
*/
public final class Promise {
private final Actor, Actor>, Promise>> actor;
private final Strategy s;
private final CountDownLatch l = new CountDownLatch(1);
private volatile Option v = none();
private final Queue> waiting = new LinkedList<>();
private Promise(final Strategy s, final Actor, Actor>, Promise>> qa) {
this.s = s;
actor = qa;
}
private static Promise mkPromise(final Strategy s) {
final Actor, Actor>, Promise>> q =
queueActor(s, new Effect1, Actor>, Promise>>() {
public void f(final P2, Actor>, Promise> p) {
final Promise snd = p._2();
final Queue> as = snd.waiting;
if (p._1().isLeft()) {
final A a = p._1().left().value()._1();
snd.v = some(a);
snd.l.countDown();
while (!as.isEmpty())
as.remove().act(a);
} else if (snd.v.isNone())
as.add(p._1().right().value());
else
p._1().right().value().act(snd.v.some());
}
});
return new Promise<>(s, q);
}
/**
* Promises to provide the value of the given 1-product, in the future.
* Represents the unit function for promises.
*
* @param s The strategy with which to fulfil the promise.
* @param a The 1-product to evaluate concurrently.
* @return A promise representing the future result of evaluating the given 1-product.
*/
public static Promise promise(final Strategy s, final P1 a) {
final Promise p = mkPromise(s);
p.actor.act(p(Either.left(a), p));
return p;
}
/**
* Provides a first-class unit function for promises.
*
* @param s The strategy with which to fulfil promises.
* @return A function that, given a 1-product, yields a promise of that product's value.
*/
public static F, Promise> promise(final Strategy s) {
return a -> promise(s, a);
}
/**
* Provides a promise to call the given Callable in the future.
*
* @param s The strategy with which to fulfil the promise.
* @param a The Callable to evaluate concurrently.
* @return A promise of a new Callable that will return the result of calling the given Callable.
*/
public static Promise> promise(final Strategy s, final Callable a) {
return promise(s, P.lazy(() -> normalise(a)));
}
/**
* Transforms any function so that it returns a promise of a value instead of an actual value.
* Represents the Kleisli arrow for the Promise monad.
*
* @param s The strategy with which to fulfil the promise.
* @param f The function to turn into a promise-valued function.
* @return The given function transformed into a function that returns a promise.
*/
public static F> promise(final Strategy s, final F f) {
return a -> promise(s, P1.curry(f).f(a));
}
/**
* Promises to send a value to the given actor in the future.
*
* @param a An actor that will receive this Promise's value in the future.
*/
public void to(final Actor a) {
actor.act(p(Either.right(a), this));
}
/**
* Provides a promise to apply the given function to this promise's future value (covariant functor pattern).
*
* @param f The function to apply to this promise's future value.
* @return A promise representing the future result of applying the given function to this promised value.
*/
public Promise fmap(final F f) {
return bind(promise(s, f));
}
/**
* Promotes any function to a transformation between promises (covariant functor pattern).
*
* @param f The function to promote to a transformation between promises.
* @return That function lifted to a function on Promises.
*/
public static F, Promise> fmap_(final F f) {
return a -> a.fmap(f);
}
/**
* Turns a promise of a promise into just a promise. The join function for the Promise monad.
* Promise to give it a Promise of an A, and it will promise you an A in return.
*
* @param p A promise of a promise.
* @return The promised promise.
*/
public static Promise join(final Promise> p) {
final F, Promise> id = identity();
return p.bind(id);
}
/**
* Turns a product of a promise into just a promise. Does not block on the product by calling it,
* but creates a new promise with a final join.
*
* @param s The strategy with which to fulfil the promise.
* @param p A product-1 of a promise to turn into just a promise.
* @return The joined promise.
*/
public static Promise join(final Strategy s, final P1> p) {
return join(promise(s, p));
}
/**
* Binds the given function over this promise, with a final join.
* The bind function for the Promise monad.
*
* @param f The function to bind over this promise.
* @return The result of applying the given function to this promised value.
*/
public Promise bind(final F> f) {
final Promise r = mkPromise(s);
final Actor ab = actor(s, new Effect1() {
public void f(final B b) {
r.actor.act(p(Either.left(p(b)), r));
}
});
to(ab.promise().contramap(f));
return r;
}
/**
* Performs function application within a promise (applicative functor pattern).
*
* @param pf The promised function to apply.
* @return A new promise after applying the given promised function to this promise.
*/
public Promise apply(final Promise> pf) {
return pf.bind(this::fmap);
}
/**
* Binds the given function to this promise and the given promise, with a final join.
*
* @param pb A promise with which to bind the given function.
* @param f The function to apply to the given promised values.
* @return A new promise after performing the map, then final join.
*/
public Promise bind(final Promise pb, final F> f) {
return pb.apply(fmap(f));
}
/**
* Binds the given function to this promise and the given promise, with a final join.
*
* @param p A promise with which to bind the given function.
* @param f The function to apply to the given promised values.
* @return A new promise after performing the map, then final join.
*/
public Promise bind(final P1> p, final F> f) {
return join(s, p).apply(fmap(f));
}
/**
* Promotes a function of arity-2 to a function on promises.
*
* @param f The function to promote.
* @return A function of arity-2 promoted to map over promises.
*/
public static F, F, Promise>> liftM2(final F> f) {
return curry((ca, cb) -> ca.bind(cb, f));
}
/**
* Turns a List of promises into a single promise of a List.
*
* @param s The strategy with which to sequence the promises.
* @param as The list of promises to transform.
* @return A single promise for the given List.
*/
public static Promise> sequence(final Strategy s, final List> as) {
return join(foldRight(s, liftM2(List.cons()), promise(s, p(List.nil()))).f(as));
}
/**
* First-class version of the sequence function through a List.
*
* @param s The strategy with which to sequence a given list of promises.
* @return A function that turns a list of promises into a single promise of a list.
*/
public static F>, Promise>> sequence(final Strategy s) {
return as -> sequence(s, as);
}
/**
* Turns a Stream of promises into a single promise of a Stream.
*
* @param s The strategy with which to sequence the promises.
* @param as The Stream of promises to transform.
* @return A single promise for the given Stream.
*/
public static Promise> sequence(final Strategy s, final Stream> as) {
return join(foldRightS(s, curry((Promise o, P1>> p) -> o.bind(a -> p._1().fmap(Stream.cons_().f(a)))), promise(s, p(Stream.nil()))).f(as));
}
/**
* First-class version of the sequence function through a Stream.
*
* @param s The strategy with which to sequence a given Stream of promises.
* @return A function that turns a list of promises into a single promise of a Stream..
*/
public static F>, Promise>> sequenceS(final Strategy s) {
return as -> sequence(s, as);
}
/**
* Transforms a product of a promise to a promise of a product.
*
* @param s The strategy with which to traverse the promise.
* @param p A product of a promise to traverse.
* @return A promised product.
*/
public static Promise> sequence(final Strategy s, final P1> p) {
return join(promise(s, p)).fmap(P.p1());
}
/**
* Performs a right-fold reduction across a list in constant stack space.
*
* @param s The strategy with which to fold the list.
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public static F, Promise> foldRight(final Strategy s, final F> f, final B b) {
return new F, Promise>() {
public Promise f(final List as) {
return as.isEmpty() ? promise(s, p(b)) : liftM2(f).f(promise(s, p(as.head()))).f(
join(s, P1.curry(this).f(as.tail())));
}
};
}
/**
* Performs a right-fold reduction across a Stream in constant stack space.
*
* @param s The strategy with which to fold the Stream.
* @param f The function to apply on each element of the Stream.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public static F, Promise> foldRightS(final Strategy s, final F, B>> f,
final B b) {
return new F, Promise>() {
public Promise f(final Stream as) {
return as.isEmpty() ? promise(s, p(b)) : liftM2(f).f(promise(s, p(as.head()))).f(
Promise.join(s, P.lazy(() -> f(as.tail()._1()).fmap(P.p1()))));
}
};
}
/**
* Waits if necessary for the computation to complete, and then retrieves its result.
*
* @return The promised value.
*/
public A claim() {
try {
l.await();
} catch (InterruptedException e) {
throw new Error(e);
}
return v.some();
}
/**
* Waits if necessary for the computation to complete, and then retrieves its result.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return The promised value, or none if the timeout was reached.
*/
public Option claim(final long timeout, final TimeUnit unit) {
try {
if (l.await(timeout, unit))
return v;
} catch (InterruptedException e) {
throw new Error(e);
}
return none();
}
/**
* Returns true if this promise has been fulfilled.
*
* @return true if this promise has been fulfilled.
*/
public boolean isFulfilled() {
return v.isSome();
}
/**
* Binds the given function across a promise of this promise (Comonad pattern).
*
* @param f A function to apply within a new promise of this promise.
* @return A new promise of the result of applying the given function to this promise.
*/
public Promise cobind(final F, B> f) {
return promise(s, P.lazy(() -> f.f(Promise.this)));
}
/**
* Duplicates this promise to a promise of itself (Comonad pattern).
*
* @return a promise of this promise.
*/
public Promise> cojoin() {
final F, Promise> id = identity();
return cobind(id);
}
/**
* Applies a stream of comonadic functions to this promise, returning a stream of values.
*
* @param fs A stream of functions to apply to this promise.
* @return A stream of the results of applying the given stream of functions to this promise.
*/
public Stream sequenceW(final Stream, B>> fs) {
return fs.isEmpty()
? Stream.nil()
: Stream.cons(fs.head().f(this), () -> sequenceW(fs.tail()._1()));
}
}