package fj; import static fj.Function.*; import fj.data.List; import fj.data.Stream; /** * A product-2. * * @version %build.number% */ public abstract class P2 { /** * Access the first element of the product. * * @return The first element of the product. */ public abstract A _1(); /** * Access the second element of the product. * * @return The second element of the product. */ public abstract B _2(); /** * Swaps the elements around in this product. * * @return A new product-2 with the elements swapped. */ public final P2 swap() { return new P2() { public B _1() { return P2.this._2(); } public A _2() { return P2.this._1(); } }; } /** * Map the first element of the product. * * @param f The function to map with. * @return A product with the given function applied. */ public final P2 map1(final F f) { return new P2() { public X _1() { return f.f(P2.this._1()); } public B _2() { return P2.this._2(); } }; } /** * Map the second element of the product. * * @param f The function to map with. * @return A product with the given function applied. */ public final P2 map2(final F f) { return new P2() { public A _1() { return P2.this._1(); } public X _2() { return f.f(P2.this._2()); } }; } /** * Split this product between two argument functions and combine their output. * * @param f A function that will map the first element of this product. * @param g A function that will map the second element of this product. * @return A new product with the first function applied to the second element * and the second function applied to the second element. */ public final P2 split(final F f, final F g) { final F, P2> ff = map1_(f); final F, P2> gg = map2_(g); return compose(ff, gg).f(this); } /** * Duplicates this product on the first element, and maps the given function across the duplicate (Comonad pattern). * * @param k A function to map over the duplicated product. * @return A new product with the result of the given function applied to this product as the first element, * and with the second element intact. */ public final P2 cobind(final F, C> k) { return new P2() { public C _1() { return k.f(P2.this); } public B _2() { return P2.this._2(); } }; } /** * Duplicates this product into the first element (Comonad pattern). * * @return A new product with this product in its first element and with the second element intact. */ public final P2, B> duplicate() { final F, P2> id = identity(); return cobind(id); } /** * Replaces the first element of this product with the given value. * * @param c The value with which to replace the first element of this product. * @return A new product with the first element replaced with the given value. */ public final P2 inject(final C c) { final F, C> co = constant(c); return cobind(co); } /** * Applies a list of comonadic functions to this product, returning a list of values. * * @param fs A list of functions to apply to this product. * @return A list of the results of applying the given list of functions to this product. */ public final List sequenceW(final List, C>> fs) { List.Buffer cs = List.Buffer.empty(); for (final F, C> f : fs) cs = cs.snoc(f.f(this)); return cs.toList(); } /** * Applies a stream of comonadic functions to this product, returning a stream of values. * * @param fs A stream of functions to apply to this product. * @return A stream of the results of applying the given stream of functions to this product. */ public final Stream sequenceW(final Stream, C>> fs) { return fs.isEmpty() ? Stream.nil() : Stream.cons(fs.head().f(this), new P1>() { public Stream _1() { return sequenceW(fs.tail()._1()); } }); } /** * Returns the 1-product projection over the first element. * * @return the 1-product projection over the first element. */ public final P1 _1_() { return P2.__1().lazy().f(this); } /** * Returns the 1-product projection over the second element. * * @return the 1-product projection over the second element. */ public final P1 _2_() { return P2.__2().lazy().f(this); } /** * Provides a memoising P2 that remembers its values. * * @return A P2 that calls this P2 once for any given element and remembers the value for subsequent calls. */ public final P2 memo() { return new P2() { private final P1 a = _1_().memo(); private final P1 b = _2_().memo(); public A _1() { return a._1(); } public B _2() { return b._1(); } }; } /** * A first-class version of the split function. * * @param f A function that will map the first element of the given product. * @param g A function that will map the second element of the given product. * @return A function that splits a given product between the two given functions and combines their output. */ public static F, P2> split_(final F f, final F g) { return new F, P2>() { public P2 f(final P2 p) { return p.split(f, g); } }; } /** * Promotes a function so that it maps the first element of a product. * * @param f The function to promote. * @return The given function, promoted to map the first element of products. */ public static F, P2> map1_(final F f) { return new F, P2>() { public P2 f(final P2 p) { return p.map1(f); } }; } /** * Promotes a function so that it maps the second element of a product. * * @param f The function to promote. * @return The given function, promoted to map the second element of products. */ public static F, P2> map2_(final F f) { return new F, P2>() { public P2 f(final P2 p) { return p.map2(f); } }; } /** * Sends the given input value to both argument functions and combines their output. * * @param f A function to receive an input value. * @param g A function to receive an input value. * @param b An input value to send to both functions. * @return The product of the two functions applied to the input value. */ public static P2 fanout(final F f, final F g, final B b) { return join(P.p2()).f(b).split(f, g); } /** * Maps the given function across both the elements of the given product. * * @param f A function to map over a product. * @param p A product over which to map. * @return A new product with the given function applied to both elements. */ public static P2 map(final F f, final P2 p) { return p.split(f, f); } /** * Returns a curried form of {@link #swap()}. * * @return A curried form of {@link #swap()}. */ public static F, P2> swap_() { return new F, P2>() { public P2 f(final P2 p) { return p.swap(); } }; } /** * Returns a function that returns the first element of a product. * * @return A function that returns the first element of a product. */ public static F, A> __1() { return new F, A>() { public A f(final P2 p) { return p._1(); } }; } /** * Returns a function that returns the second element of a product. * * @return A function that returns the second element of a product. */ public static F, B> __2() { return new F, B>() { public B f(final P2 p) { return p._2(); } }; } /** * Transforms a curried function of arity-2 to a function of a product-2 * * @param f a curried function of arity-2 to transform into a function of a product-2 * @return The function, transformed to operate on on a product-2 */ public static F, C> tuple(final F> f) { return new F, C>() { public C f(final P2 p) { return f.f(p._1()).f(p._2()); } }; } /** * Transforms an uncurried function of arity-2 to a function of a product-2 * * @param f an uncurried function of arity-2 to transform into a function of a product-2 * @return The function, transformed to operate on on a product-2 */ public static F, C> tuple(final F2 f) { return tuple(curry(f)); } /** * Transforms a function of a product-2 to an uncurried function or arity-2. * * @param f A function of a product-2 to transform into an uncurried function. * @return The function, transformed to an uncurried function of arity-2. */ public static F2 untuple(final F, C> f) { return new F2() { public C f(final A a, final B b) { return f.f(P.p(a, b)); } }; } }