
fj.control.parallel.Strategy Maven / Gradle / Ivy
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()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy