All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.control.parallel.ParModule Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.control.parallel;

import fj.*;

import static fj.P.p;
import static fj.Function.curry;
import static fj.Function.uncurryF2;
import static fj.control.parallel.Promise.liftM2;
import fj.data.Array;
import fj.data.IterableW;
import fj.data.List;
import fj.data.NonEmptyList;
import fj.data.Option;
import fj.data.Stream;
import fj.data.Tree;
import fj.data.TreeZipper;
import fj.data.Zipper;
import fj.function.Effect1;

import static fj.data.Option.some;
import static fj.data.Stream.iterableStream;

/**
 * A module of higher-order concurrency features.
 */
public final class ParModule {
  private final Strategy strategy;

  private ParModule(final Strategy strategy) {
    this.strategy = strategy;
  }

  /**
   * Constructor method for ParModule
   *
   * @param u A parallel strategy for the module.
   * @return A ParModule that uses the given strategy for parallelism.
   */
  public static ParModule parModule(final Strategy u) {
    return new ParModule(u);
  }

  /**
   * Evaluates the given product concurrently and returns a Promise of the result.
   *
   * @param p A product to evaluate concurrently.
   * @return A Promise of the value of the given product, that can be claimed in the future.
   */
  public  Promise promise(final P1 p) {
    return Promise.promise(strategy, p);
  }

  /**
   * Returns a function that evaluates a given product concurrently and returns a Promise of the result.
   *
   * @return a function that evaluates a given product concurrently and returns a Promise of the result.
   */
  public  F, Promise> promise() {
    return this::promise;
  }

  /**
   * Promotes the given function to a concurrent function that returns a Promise.
   *
   * @param f A given function to promote to a concurrent function.
   * @return A function that is applied concurrently when given an argument, yielding a Promise of the result
   *         that can be claimed in the future.
   */
  public  F> promise(final F f) {
    return F1Functions.promiseK(f, strategy);
  }

  /**
   * Returns a function that promotes a given function to a concurrent function that returns a Promise.
   * The pure Kleisli arrow of Promise.
   *
   * @return A higher-order function that takes pure functions to promise-valued functions.
   */
  public  F, F>> promisePure() {
    return this::promise;
  }

  /**
   * Promotes the given function to a concurrent function that returns a Promise.
   *
   * @param f A given function to promote to a concurrent function.
   * @return A function that is applied concurrently when given an argument, yielding a Promise of the result
   *         that can be claimed in the future.
   */
  public  F2> promise(final F2 f) {
    return P2.untuple(F1Functions.promiseK(F2Functions.tuple(f), strategy));
  }


  /**
   * Creates a very fast concurrent effect, as an actor that does not guarantee ordering of its messages.
   * Such an actor is not thread-safe unless the given Effect is.
   *
   * @param e The effect that the actor should have on its messages.
   * @return A concurrent actor that does not guarantee ordering of its messages.
   */
  public  Actor effect(final Effect1 e) {
    return Actor.actor(strategy, e);
  }

  /**
   * A first-class constructor of concurrent effects, as actors that don't guarantee ordering of messages.
   * Such an actor is not thread-safe unless the given Effect is.
   *
   * @return A function that takes an effect and returns a concurrent effect.
   */
  public  F, Actor> effect() {
    return this::effect;
  }

  /**
   * Creates a concurrent actor that is guaranteed to process only one message at a time.
   *
   * @param e The effect that the actor should have on its messages.
   * @return A concurrent actor that is guaranteed to process its messages in order.
   */
  public  Actor actor(final Effect1 e) {
    return Actor.queueActor(strategy, e);
  }

  /**
   * A first-class constructor of actors.
   *
   * @return A function that takes an effect and returns an actor that processes messages in some order.
   */
  public  F, Actor> actor() {
    return this::actor;
  }

  /**
   * List iteration inside a Promise. Traverses a List of Promises yielding a Promise of a List.
   *
   * @param ps A list of promises to sequence.
   * @return A promise of the List of values promised by the list of promises.
   */
  public  Promise> sequence(final List> ps) {
    return Promise.sequence(strategy, ps);
  }

  /**
   * A first-class function that traverses a list inside a promise.
   *
   * @return A first-class function that traverses a list inside a promise.
   */
  public  F>, Promise>> sequenceList() {
    return this::sequence;
  }

  /**
   * Stream iteration inside a Promise. Traverses a Stream of Promises yielding a Promise of a Stream.
   *
   * @param ps A Stream of promises to sequence.
   * @return A promise of the Stream of values promised by the Stream of promises.
   */
  public  Promise> sequence(final Stream> ps) {
    return Promise.sequence(strategy, ps);
  }

  /**
   * A first-class function that traverses a stream inside a promise.
   *
   * @return A first-class function that traverses a stream inside a promise.
   */
  public  F>, Promise>> sequenceStream() {
    return this::sequence;
  }

  /**
   * Traverses a product-1 inside a promise.
   *
   * @param p A product-1 of a promised value.
   * @return A promise of a product of the value promised by the argument.
   */
  public  Promise> sequence(final P1> p) {
    return Promise.sequence(strategy, p);
  }

  /**
   * Takes a Promise-valued function and applies it to each element
   * in the given List, yielding a promise of a List of results.
   *
   * @param as A list to map across.
   * @param f  A promise-valued function to map across the list.
   * @return A Promise of a new list with the given function applied to each element.
   */
  public  Promise> mapM(final List as, final F> f) {
    return sequence(as.map(f));
  }

  /**
   * First-class function that maps a concurrent function over a List inside a promise.
   *
   * @return a function that maps a concurrent function over a List inside a promise.
   */
  public  F>, F, Promise>>> mapList() {
    return curry((f, list) -> mapM(list, f));
  }

  /**
   * Takes a Promise-valued function and applies it to each element
   * in the given Stream, yielding a promise of a Stream of results.
   *
   * @param as A Stream to map across.
   * @param f  A promise-valued function to map across the Stream.
   * @return A Promise of a new Stream with the given function applied to each element.
   */
  public  Promise> mapM(final Stream as, final F> f) {
    return sequence(as.map(f));
  }

  /**
   * First-class function that maps a concurrent function over a Stream inside a promise.
   *
   * @return a function that maps a concurrent function over a Stream inside a promise.
   */
  public  F>, F, Promise>>> mapStream() {
    return curry((f, stream) -> mapM(stream, f));
  }

  /**
   * Maps a concurrent function over a Product-1 inside a Promise.
   *
   * @param a A product-1 across which to map.
   * @param f A concurrent function to map over the product inside a promise.
   * @return A promised product of the result of mapping the given function over the given product.
   */
  public  Promise> mapM(final P1 a, final F> f) {
    return sequence(a.map(f));
  }

  /**
   * Maps across a list in parallel.
   *
   * @param as A list to map across in parallel.
   * @param f  A function to map across the given list.
   * @return A Promise of a new list with the given function applied to each element.
   */
  public  Promise> parMap(final List as, final F f) {
    return mapM(as, promise(f));
  }

  /**
   * A first-class function that maps another function across a list in parallel.
   *
   * @return A function that maps another function across a list in parallel.
   */
  public  F, F, Promise>>> parMapList() {
    return curry((abf, list) -> parMap(list, abf));
  }

  /**
   * Maps across a nonempty list in parallel.
   *
   * @param as A NonEmptyList to map across in parallel.
   * @param f  A function to map across the given NonEmptyList.
   * @return A Promise of a new NonEmptyList with the given function applied to each element.
   */
  public  Promise> parMap(final NonEmptyList as, final F f) {
    return mapM(as.toList(), promise(f)).fmap(list -> NonEmptyList.fromList(list).some());
  }

  /**
   * Maps across a Stream in parallel.
   *
   * @param as A Stream to map across in parallel.
   * @param f  A function to map across the given Stream.
   * @return A Promise of a new Stream with the given function applied to each element.
   */
  public  Promise> parMap(final Stream as, final F f) {
    return mapM(as, promise(f));
  }

  /**
   * A first-class function that maps another function across a stream in parallel.
   *
   * @return A function that maps another function across a stream in parallel.
   */
  public  F, F, Promise>>> parMapStream() {
    return curry((abf, stream) -> parMap(stream, abf));
  }

  /**
   * Maps across an Iterable in parallel.
   *
   * @param as An Iterable to map across in parallel.
   * @param f  A function to map across the given Iterable.
   * @return A Promise of a new Iterable with the given function applied to each element.
   */
  public  Promise> parMap(final Iterable as, final F f) {
    return parMap(iterableStream(as), f)
        .fmap(Function.vary(Function.identity()));
  }

  /**
   * A first-class function that maps another function across an iterable in parallel.
   *
   * @return A function that maps another function across an iterable in parallel.
   */
  public  F, F, Promise>>> parMapIterable() {
    return curry((abf, iterable) -> parMap(iterable, abf));
  }

  /**
   * Maps across an Array in parallel.
   *
   * @param as An array to map across in parallel.
   * @param f  A function to map across the given Array.
   * @return A Promise of a new Array with the given function applied to each element.
   */
  public  Promise> parMap(final Array as, final F f) {
    return parMap(as.toStream(), f).fmap(Stream::toArray);
  }

  /**
   * A first-class function that maps another function across an array in parallel.
   *
   * @return A function that maps another function across an array in parallel.
   */
  public  F, F, Promise>>> parMapArray() {
    return curry((abf, array) -> parMap(array, abf));
  }

  /**
   * Maps a function across a Zipper in parallel.
   *
   * @param za A Zipper to map across in parallel.
   * @param f  A function to map across the given Zipper.
   * @return A promise of a new Zipper with the given function applied to each element.
   */
  public  Promise> parMap(final Zipper za, final F f) {
    return parMap(za.rights(), f)
        .apply(promise(f).f(za.focus()).apply(parMap(za.lefts(), f).fmap(curry(Zipper.zipper()))));
  }

  /**
   * Maps a function across a Tree in parallel.
   *
   * @param ta A Tree to map across in parallel.
   * @param f  A function to map across the given Tree.
   * @return A promise of a new Tree with the given function applied to each element.
   */
  public  Promise> parMap(final Tree ta, final F f) {
    return mapM(ta.subForest(), this., Tree>mapStream().f(this.parMapTree().f(f)))
        .apply(promise(f).f(ta.root()).fmap(Tree.node()));
  }

  /**
   * A first-class function that maps across a Tree in parallel.
   *
   * @return A function that maps a given function across a Tree in parallel.
   */
  public  F, F, Promise>>> parMapTree() {
    return curry((abf, tree) -> parMap(tree, abf));
  }

  /**
   * Maps a function across a TreeZipper in parallel.
   *
   * @param za A TreeZipper to map across in parallel.
   * @param f  A function to map across the given TreeZipper.
   * @return A promise of a new TreeZipper with the given function applied to each element of the tree.
   */
  public  Promise> parMap(final TreeZipper za, final F f) {
    final F, Tree> tf = Tree.fmap_().f(f);
    final P4, Stream>, Stream>, Stream>, A, Stream>>>> p = za.p();
    return mapM(p._4(),
            p3 -> parMap(p3._3(), tf).apply(promise(f).f(p3._2()).apply(
                parMap(p3._1(), tf).fmap(P.p3())))).apply(parMap(za.rights(), tf).apply(
        parMap(za.lefts(), tf).apply(parMap(p._1(), f).fmap(TreeZipper.treeZipper()))));
  }

  /**
   * Binds a list-valued function across a list in parallel, concatenating the results into a new list.
   *
   * @param as A list to bind across in parallel.
   * @param f  A function to bind across the given list in parallel.
   * @return A promise of a new List with the given function bound across its elements.
   */
  public  Promise> parFlatMap(final List as, final F> f) {
    return parFoldMap(as, f, Monoid.listMonoid());
  }

  /**
   * Binds a Stream-valued function across a Stream in parallel, concatenating the results into a new Stream.
   *
   * @param as A Stream to bind across in parallel.
   * @param f  A function to bind across the given Stream in parallel.
   * @return A promise of a new Stream with the given function bound across its elements.
   */
  public  Promise> parFlatMap(final Stream as, final F> f) {
    return parFoldMap(as, f, Monoid.streamMonoid());
  }

  /**
   * Binds an Array-valued function across an Array in parallel, concatenating the results into a new Array.
   *
   * @param as An Array to bind across in parallel.
   * @param f  A function to bind across the given Array in parallel.
   * @return A promise of a new Array with the given function bound across its elements.
   */
  public  Promise> parFlatMap(final Array as, final F> f) {
    return parMap(as, f).fmap(Array.join());
  }

  /**
   * Binds an Iterable-valued function across an Iterable in parallel, concatenating the results into a new Iterable.
   *
   * @param as A Iterable to bind across in parallel.
   * @param f  A function to bind across the given Iterable in parallel.
   * @return A promise of a new Iterable with the given function bound across its elements.
   */
  public  Promise> parFlatMap(final Iterable as, final F> f) {
    return parMap(as, f).fmap(IterableW.join())
        .fmap(Function.vary(Function.>identity()));
  }

  /**
   * Zips two lists together with a given function, in parallel.
   *
   * @param as A list to zip with another in parallel.
   * @param bs A list to zip with another in parallel.
   * @param f  A function with which to zip two lists in parallel.
   * @return A Promise of a new list with the results of applying the given function across the two lists in lockstep.
   */
  public  Promise> parZipWith(final List as, final List bs, final F> f) {
    return sequence(as.zipWith(bs, promise(uncurryF2(f))));
  }

  /**
   * Zips two streams together with a given function, in parallel.
   *
   * @param as A stream to zip with another in parallel.
   * @param bs A stream to zip with another in parallel.
   * @param f  A function with which to zip two streams in parallel.
   * @return A Promise of a new stream with the results of applying the given function across the two streams, stepwise.
   */
  public  Promise> parZipWith(final Stream as, final Stream bs, final F> f) {
    return sequence(as.zipWith(bs, promise(uncurryF2(f))));
  }

  /**
   * Zips two arrays together with a given function, in parallel.
   *
   * @param as An array to zip with another in parallel.
   * @param bs An array to zip with another in parallel.
   * @param f  A function with which to zip two arrays in parallel.
   * @return A Promise of a new array with the results of applying the given function across the two arrays, stepwise.
   */
  public  Promise> parZipWith(final Array as, final Array bs, final F> f) {
    return parZipWith(as.toStream(), bs.toStream(), f).fmap(Stream::toArray);
  }

  /**
   * Zips two iterables together with a given function, in parallel.
   *
   * @param as An iterable to zip with another in parallel.
   * @param bs An iterable to zip with another in parallel.
   * @param f  A function with which to zip two iterables in parallel.
   * @return A Promise of a new iterable with the results of applying the given function across the two iterables, stepwise.
   */
  public  Promise> parZipWith(final Iterable as, final Iterable bs, final F> f) {
    return parZipWith(iterableStream(as), iterableStream(bs), f).fmap(
        Function.vary(Function.>identity()));
  }

  /**
   * Maps with the given function across the given stream in parallel, while folding with
   * the given monoid.
   *
   * @param as     A stream to map over and reduce.
   * @param map    The function to map over the given stream.
   * @param reduce The monoid with which to sum the results.
   * @return A promise of a result of mapping and folding in parallel.
   */
  public  Promise parFoldMap(final Stream as, final F map, final Monoid reduce) {
    return as.isEmpty() ? promise(p(reduce.zero())) : as.map(promise(map)).foldLeft1(liftM2(reduce.sum()));
  }

  /**
   * Maps with the given function across chunks of the given stream in parallel, while folding with
   * the given monoid. The stream is split into chunks according to the given chunking function,
   * the given map function is mapped over all chunks simultaneously, but over each chunk sequentially.
   * All chunks are summed concurrently and the sums are then summed sequentially.
   *
   * @param as       A stream to chunk, then map over and reduce.
   * @param map      The function to map over the given stream.
   * @param reduce   The monoid with which to sum the results.
   * @param chunking A function describing how the stream should be split into chunks. Should return the first chunk
   *                 and the rest of the stream.
   * @return A promise of a result of mapping and folding in parallel.
   */
  public  Promise parFoldMap(final Stream as, final F map, final Monoid reduce,
                                      final F, P2, Stream>> chunking) {
    return parMap(Stream.unfold(stream -> stream.isEmpty() ? Option.none() : some(chunking.f(stream)), as), Stream.map_().f(map)).bind(stream -> parMap(stream, reduce.sumLeftS()).fmap(reduce.sumLeftS()));
  }

  /**
   * Maps with the given function across chunks of the given Iterable in parallel, while folding with
   * the given monoid. The Iterable is split into chunks according to the given chunking function,
   * the given map function is mapped over all chunks simultaneously, but over each chunk sequentially.
   * All chunks are summed concurrently and the sums are then summed sequentially.
   *
   * @param as       An Iterable to chunk, then map over and reduce.
   * @param map      The function to map over the given Iterable.
   * @param reduce   The monoid with which to sum the results.
   * @param chunking A function describing how the Iterable should be split into chunks. Should return the first chunk
   *                 and the rest of the Iterable.
   * @return A promise of a result of mapping and folding in parallel.
   */
  public  Promise parFoldMap(final Iterable as, final F map, final Monoid reduce,
                                      final F, P2, Iterable>> chunking) {
    return parFoldMap(iterableStream(as), map, reduce, (Stream stream) -> {
      final F, Stream> is = Stream::iterableStream;
      return chunking.f(stream).map1(is).map2(is);
    });
  }

  /**
   * Maps with the given function across the given iterable in parallel, while folding with
   * the given monoid.
   *
   * @param as     An Iterable to map over and reduce.
   * @param map    The function to map over the given Iterable.
   * @param reduce The Monoid with which to sum the results.
   * @return A promise of a result of mapping and folding in parallel.
   */
  public  Promise parFoldMap(final Iterable as, final F map, final Monoid reduce) {
    return parFoldMap(iterableStream(as), map, reduce);
  }


  /**
   * Maps the given function across all positions of the given zipper in parallel.
   *
   * @param za A zipper to extend the given function across.
   * @param f  A function to extend across the given zipper.
   * @return A promise of a new zipper of the results of applying the given function to all positions of the given
   *         zipper.
   */
  public  Promise> parExtend(final Zipper za, final F, B> f) {
    return parMap(za.positions(), f);
  }

  /**
   * Maps the given function across all subtrees of the given Tree in parallel.
   *
   * @param ta A tree to extend the given function across.
   * @param f  A function to extend across the given Tree.
   * @return A promise of a new Tree of the results of applying the given function to all subtrees of the given Tree.
   */
  public  Promise> parExtend(final Tree ta, final F, B> f) {
    return parMap(ta.cojoin(), f);
  }

  /**
   * Maps the given function across all positions of the given TreeZipper in parallel.
   *
   * @param za A TreeZipper to extend the given function across.
   * @param f  A function to extend across the given TreeZipper.
   * @return A promise of a new TreeZipper of the results of applying the given function to all positions of the
   *         given TreeZipper.
   */
  public  Promise> parExtend(final TreeZipper za, final F, B> f) {
    return parMap(za.positions(), f);
  }

  /**
   * Maps the given function across all sublists of the given NonEmptyList in parallel.
   *
   * @param as A NonEmptyList to extend the given function across.
   * @param f  A function to extend across the given NonEmptyList
   * @return A promise of a new NonEmptyList of the results of applying the given function to all sublists of the
   *         given NonEmptyList.
   */
  public  Promise> parExtend(final NonEmptyList as, final F, B> f) {
    return parMap(as.tails(), f);
  }

}