fj.control.parallel.Strategy 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.F;
import fj.F2;
import fj.Function;
import fj.P;
import fj.P1;
import static fj.Function.compose;
import static fj.Function.curry;
import fj.data.Java;
import fj.data.List;
import fj.data.Array;
import fj.function.Effect1;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* Functional-style parallel evaluation strategies.
* A Strategy is a method of evaluating a product-1, yielding another product-1 from which the result of its evaluation
* can be retrieved at a later time.
*
*
* @version %build.number%
*/
public final class Strategy {
private final F, P1> f;
private Strategy(final F, P1> f) {
this.f = f;
}
/**
* Returns the functional representation of this Strategy, a function that evaluates a product-1.
*
* @return The function representing this strategy, which evaluates a product-1.
*/
public F, P1> f() {
return f;
}
/**
* Constructs a strategy from the given evaluation function.
*
* @param f The execution function for the strategy
* @return A strategy that uses the given function to evaluate product-1s.
*/
public static Strategy strategy(final F, P1> f) {
return new Strategy<>(f);
}
/**
* Apply the strategy to the given product-1.
*
* @param a A P1 to evaluate according to this strategy.
* @return A P1 that yields the value from calling the given product-1.
*/
public P1 par(final P1 a) {
return f().f(a);
}
/**
* Promotes a function to a concurrent function.
*
* @param f A function to promote to a concurrent function.
* @return A function that executes concurrently when called, yielding a Future value.
*/
public F> concurry(final F f) {
return compose(f(), P1.curry(f));
}
/**
* Promotes a function of arity-2 to a concurrent function.
*
* @param f The function to promote to a concurrent function.
* @return A function that executes concurrently when called, yielding a product-1 that returns the value.
*/
public F>> concurry(final F2 f) {
return b -> concurry(curry(f).f(b));
}
/**
* Waits for every Future in a list to obtain a value, and collects those values in a list.
*
* @param xs The list of Futures from which to get values.
* @return A list of values extracted from the Futures in the argument list.
*/
public static List> mergeAll(final List> xs) {
return xs.map(Strategy.obtain());
}
/**
* Evaluates a list of product-1s in parallel.
*
* @param ps A list to evaluate in parallel.
* @return A list of the values of the product-1s in the argument.
*/
public P1> parList(final List> ps) {
return P1.sequence(ps.map(f()));
}
/**
* Maps the given function over the given list in parallel using this strategy.
*
* @param f A function to map over the given list in parallel.
* @param bs A list over which to map the given function in parallel.
* @return A product-1 that returns the list with all of its elements transformed by the given function.
*/
public P1> parMap(final F f, final List bs) {
return P1.sequence(bs.map(concurry(f)));
}
/**
* Maps the given function over the given array in parallel using this strategy.
*
* @param f A function to map over the given array in parallel.
* @param bs An array over which to map the given function in parallel.
* @return A product-1 that returns the array with all of its elements transformed by the given function.
*/
public P1> parMap(final F f, final Array bs) {
return P1.sequence(bs.map(concurry(f)));
}
/**
* A strict version of parMap over lists.
* Maps the given function over the given list in parallel using this strategy,
* blocking the current thread until all values have been obtained.
*
* @param f A function to map over the given list in parallel.
* @param bs A list over which to map the given function in parallel.
* @return A list with all of its elements transformed by the given function.
*/
public List parMap1(final F f, final List bs) {
return compose(P1.__1(), parMapList(f)).f(bs);
}
/**
* A strict version of parMap over arrays.
* Maps the given function over the given arrays in parallel using this strategy,
* blocking the current thread until all values have been obtained.
*
* @param f A function to map over the given array in parallel.
* @param bs An array over which to map the given function in parallel.
* @return An array with all of its elements transformed by the given function.
*/
public Array parMap1(final F f, final Array bs) {
return compose(P1.__1(), parMapArray(f)).f(bs);
}
/**
* Promotes a function to a parallel function on lists using this strategy.
*
* @param f A function to transform into a parallel function on lists.
* @return The function transformed into a parallel function on lists.
*/
public F, P1>> parMapList(final F f) {
return as -> parMap(f, as);
}
/**
* First-class version of parMap on lists.
*
* @return A function that promotes another function to a parallel function on lists.
*/
public F, F, P1>>> parMapList() {
return this::parMapList;
}
/**
* First-class version of parMap1 on lists (parallel list functor).
*
* @return A function that promotes another function to a blocking parallel function on lists.
*/
public F, F, List>> parMapList1() {
return f1 -> bs -> parMap1(f1, bs);
}
/**
* Promotes a function to a parallel function on arrays using this strategy.
*
* @param f A function to transform into a parallel function on arrays.
* @return The function transformed into a parallel function on arrays.
*/
public F, P1>> parMapArray(final F f) {
return as -> parMap(f, as);
}
/**
* First-class version of parMap on arrays.
*
* @return A function that promotes another function to a parallel function on arrays.
*/
public F, F, P1>>> parMapArray() {
return this::parMapArray;
}
/**
* First-class version of parMap1 on arrays (parallel array functor).
*
* @return A function that promotes another function to a blocking parallel function on arrays.
*/
public F, F, Array>> parMapArray1() {
return f1 -> bs -> parMap1(f1, bs);
}
/**
* Binds the given function in parallel across the given list, using the given strategy, with a final join.
*
* @param s The strategy to use for parallelization.
* @param f The function to bind across the given list.
* @param as The list across which to bind the given function.
* @return A P1 containing the result of the parallel map operation after the final join.
*/
public static P1> parFlatMap(final Strategy> s,
final F> f,
final List as) {
return P1.map_(List.join()).f(s.parMap(f, as));
}
/**
* Binds the given function in parallel across the given array, using the given strategy, with a final join.
*
* @param s The strategy to use for parallelization.
* @param f The function to bind across the given array.
* @param as The array across which to bind the given function.
* @return A P1 containing the result of the parallel map operation after the final join.
*/
public static P1> parFlatMap(final Strategy> s,
final F> f,
final Array as) {
return P1.map_(Array.join()).f(s.parMap(f, as));
}
/**
* Sequentially evaluates chunks (sub-sequences) of a list in parallel. Splits the list into chunks,
* evaluating the chunks simultaneously, but each chunk as a sequence.
*
* @param s The strategy to use for parallelization.
* @param chunkLength The length of each sequence.
* @param as The list to evaluate in parallel chunks.
* @return A product-1 containing the list of results extracted from the given list of product-1s.
*/
public static P1> parListChunk(final Strategy> s,
final int chunkLength,
final List> as) {
return P1.map_(List.join()).f(s.parList(as.partition(chunkLength).map(P1.sequenceList())));
}
/**
* Zips together two lists in parallel using a given function, with this strategy.
* Calls the given function once for each corresponding pair in the lists, position-wise,
* passing elements from the first list to the first argument of the function, and elements from the second list
* to the second argument of the function, yielding a list of the results.
* If the lists are not of the same length, the remaining elements of the longer list are ignored.
*
* @param f The function of arity-2 with which to zip.
* @param bs A list to zip with the given function.
* @param cs A list to zip with the given function.
* @return The list of the results of calling the given function on corresponding elements of the given lists.
*/
public P1> parZipWith(final F2 f, final List bs, final List cs) {
return P1.sequence(bs.zipWith(cs, concurry(f)));
}
/**
* Zips together two arrays in parallel using a given function, with this strategy.
* Calls the given function once for each corresponding pair in the arrays, position-wise,
* passing elements from the first array to the first argument of the function, and elements from the second array
* to the second argument of the function, yielding a array of the results.
* If the arrays are not of the same length, the remaining elements of the longer array are ignored.
*
* @param f The function of arity-2 with which to zip.
* @param bs A array to zip with the given function.
* @param cs A array to zip with the given function.
* @return The array of the results of calling the given function on corresponding elements of the given arrays.
*/
public P1> parZipWith(final F2 f, final Array bs, final Array cs) {
return P1.sequence(bs.zipWith(cs, concurry(f)));
}
/**
* Lifts a given function of arity-2 so that it zips together two lists in parallel,
* using this strategy, calling the function once for each corresponding pair in the lists, position-wise.
*
* @param f The function of arity-2 with which to zip.
* @return A transformation that zips two lists using the argument function, in parallel.
*/
public F2, List, P1>> parZipListWith(final F2 f) {
return (bs, cs) -> parZipWith(f, bs, cs);
}
/**
* Lifts a given function of arity-2 so that it zips together two arrays in parallel,
* using this strategy, calling the function once for each corresponding pair in the arrays, position-wise.
*
* @param f The function of arity-2 with which to zip.
* @return A transformation that zips two arrays using the argument function, in parallel.
*/
public F2, Array, P1>> parZipArrayWith(final F2 f) {
return (bs, cs) -> parZipWith(f, bs, cs);
}
/**
* Returns a function which returns a product-1 which waits for the given Future to obtain a value.
*
* @return A function which, given a Future, yields a product-1 that waits for it.
*/
public static F, P1> obtain() {
return Strategy::obtain;
}
/**
* Provides a product-1 that waits for the given future to obtain a value.
*
* @param t A Future for which to wait.
* @return A product-1 that waits for the given future to obtain a value.
*/
public static P1 obtain(final Future t) {
return P.lazy(() -> {
try {
return t.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new Error(e);
} catch (ExecutionException e) {
throw new Error(e);
}
});
}
/**
* Returns an Effect that waits for a given Future to obtain a value, discarding the value.
*
* @return An effect, which, given a Future, waits for it to obtain a value, discarding the value.
*/
public static Effect1> discard() {
return a -> Strategy.obtain().f(a)._1();
}
/**
* Provides a simple parallelization strategy that creates, and discards, a new thread for
* every evaluation.
*
* @return a simple parallelization strategy that creates, and discards, a new thread for
* every evaluation.
*/
public static Strategy simpleThreadStrategy() {
return strategy(p -> {
final FutureTask t = new FutureTask<>(Java.P1_Callable().f(p));
new Thread(t).start();
return obtain(t);
});
}
/**
* Provides a parallelization strategy that uses an ExecutorService to control the method and
* degree of parallelism.
*
* @param s The ExecutorService to use for scheduling evaluations.
* @return A Strategy that uses the provided ExecutorService to control the method and degree
* of parallelism.
*/
public static Strategy executorStrategy(final ExecutorService s) {
return strategy(p -> obtain(s.submit(Java.P1_Callable().f(p))));
}
/**
* Provides a parallelization strategy that uses a CompletionService to control the method and
* degree of parallelism, and where each parallel task's completion is registered with the service.
*
* @param s The CompletionService to use for scheduling evaluations and detect their completion.
* @return A Strategy that uses the provided CompletionService to control the method and degree of parallelism,
* and notifies the service of task completion.
*/
public static Strategy completionStrategy(final CompletionService s) {
return strategy(p -> obtain(s.submit(Java.P1_Callable().f(p))));
}
/**
* Provides a strategy that performs sequential (non-concurrent) evaluation of its argument.
*
* @return A strategy that performs sequential (non-concurrent) evaluation of its argument.
*/
public static Strategy seqStrategy() {
return strategy(a -> P.p(a._1()));
}
/**
* Provides a strategy that performs no evaluation of its argument.
*
* @return A strategy that performs no evaluation of its argument.
*/
public static Strategy idStrategy() {
return strategy(Function.identity());
}
/**
* Maps the given bijective transformation across this strategy (Exponential Functor pattern).
*
* @param f A transformation from this strategy's codomain to the resulting strategy's codomain.
* @param g A transformation from the resulting strategy's domain to this strategy's domain.
* @return A new strategy that maps to this strategy and back again.
*/
public Strategy xmap(final F, P1> f, final F, P1> g) {
return strategy(compose(f, compose(f(), g)));
}
/**
* Maps the given transformation across this strategy's domain (Invariant Functor pattern).
*
* @param f A transformation from this strategy's codomain to the resulting strategy's codomain.
* @return A new strategy that applies the given transformation after each application of this strategy.
*/
public Strategy map(final F, P1> f) {
return xmap(f, Function.identity());
}
/**
* Maps the given transformation across this strategy's codomain (Invariant Functor pattern).
*
* @param f A transformation from the resulting strategy's domain to this strategy's domain.
* @return A new strategy that applies the given transformation before each application of this strategy.
*/
public Strategy contramap(final F, P1> f) {
return xmap(Function.identity(), f);
}
/**
* Provides an error-handling strategy. Captures any uncaught runtime errors encountered by this strategy and applies
* the given side-effect to them.
*
* @param e The effect that should handle errors.
* @return A strategy that captures any runtime errors with a side-effect.
*/
public Strategy errorStrategy(final Effect1 e) {
return errorStrategy(this, e);
}
/**
* Provides an error-handling strategy. Captures any uncaught runtime errors encountered by the given strategy
* and applies the given side-effect to them.
*
* @param s The strategy to equip with an error-handling effect.
* @param e The effect that should handle errors.
* @return A strategy that captures any runtime errors with a side-effect.
*/
public static Strategy errorStrategy(final Strategy s, final Effect1 e) {
return s.contramap(a -> P.lazy(() -> {
try {
return a._1();
} catch (Throwable t) {
final Error error = new Error(t);
e.f(error);
throw error;
}
})
);
}
/**
* Provides a normalising strategy that fully evaluates its Callable argument.
*
* @param s A non-normalising strategy to use for the evaluation.
* @return A new strategy that fully evaluates Callables, using the given strategy.
*/
public static Strategy> callableStrategy(final Strategy> s) {
return s.contramap(a -> P1.curry(Callables.normalise()).f(a._1()));
}
}