package fj; import fj.data.List; import fj.data.Stream; import fj.data.Array; import java.lang.ref.SoftReference; /** * A product-1. Also, the identity monad. * * @version %build.number% */ public abstract class P1 { /** * Access the first element of the product. * * @return The first element of the product. */ public abstract A _1(); /** * Map the element of the product. * * @param f The function to map with. * @return A product with the given function applied. */ public final P1 map(final F f) { return new P1() { public X _1() { return f.f(P1.this._1()); } }; } /** * 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 P1 p) { return p._1(); } }; } /** * Promote any function to a transformation between P1s. * * @param f A function to promote to a transformation between P1s. * @return A function promoted to operate on P1s. */ public static F, P1> fmap(final F f) { return new F, P1>() { public P1 f(final P1 a) { return a.map(f); } }; } /** * Binds the given function to the value in a product-1 with a final join. * * @param a A value in a product-1 to which to apply a function. * @param f A function to apply to the value in a product-1. * @return The result of applying the given function to the value of given product-1. */ public static P1 bind(final P1 a, final F> f) { return new P1() { public B _1() { return f.f(a._1())._1(); } }; } /** * Promotes the given function so that it returns its value in a P1. * * @param f A function to have its result wrapped in a P1. * @return A function whose result is wrapped in a P1. */ public static F> curry(final F f) { return new F>() { public P1 f(final A a) { return new P1() { public B _1() { return f.f(a); } }; } }; } /** * Performs function application within a P1 (applicative functor pattern). * * @param ca The P1 to which to apply a function. * @param cf The P1 function to apply. * @return A new P1 after applying the given P1 function to the first argument. */ public static P1 apply(final P1 ca, final P1> cf) { return bind(cf, new F, P1>() { public P1 f(final F f) { return fmap(f).f(ca); } }); } /** * Binds the given function to the values in the given P1s with a final join. * * @param ca A given P1 to bind the given function with. * @param cb A given P1 to bind the given function with. * @param f The function to apply to the values in the given P1s. * @return A new P1 after performing the map, then final join. */ public static P1 bind(final P1 ca, final P1 cb, final F> f) { return apply(cb, fmap(f).f(ca)); } /** * Joins a P1 of a P1 with a bind operation. * * @param a The P1 of a P1 to join. * @return A new P1 that is the join of the given P1. */ public static P1 join(final P1> a) { return bind(a, Function.>identity()); } /** * Promotes a function of arity-2 to a function on P1s. * * @param f The function to promote. * @return A function of arity-2 promoted to map over P1s. */ public static F, F, P1>> liftM2(final F> f) { return Function.curry(new F2, P1, P1>() { public P1 f(final P1 pa, final P1 pb) { return bind(pa, pb, f); } }); } /** * Turns a List of P1s into a single P1 of a List. * * @param as The list of P1s to transform. * @return A single P1 for the given List. */ public static P1> sequence(final List> as) { return as.foldRight(liftM2(List.cons()), P.p(List.nil())); } /** * A first-class version of the sequence method for lists of P1s. * * @return A function from a List of P1s to a single P1 of a List. */ public static F>, P1>> sequenceList() { return new F>, P1>>() { public P1> f(final List> as) { return sequence(as); } }; } /** * Turns a stream of P1s into a single P1 of a stream. * * @param as The stream of P1s to transform. * @return A single P1 for the given stream. */ public static P1> sequence(final Stream> as) { return as.foldRight(liftM2(Stream.cons()), P.p(Stream.nil())); } /** * Turns an array of P1s into a single P1 of an array. * * @param as The array of P1s to transform. * @return A single P1 for the given array. */ public static P1> sequence(final Array> as) { return new P1>() { public Array _1() { return as.map(P1.__1()); } }; } /** * Provides a memoising P1 that remembers its value. * * @return A P1 that calls this P1 once and remembers the value for subsequent calls. */ public final P1 memo() { final P1 self = this; return new P1() { private final Object latch = new Object(); @SuppressWarnings({"InstanceVariableMayNotBeInitialized"}) private volatile SoftReference v; public A _1() { A a = v != null ? v.get() : null; if (a == null) synchronized (latch) { if (v == null || v.get() == null) a = self._1(); v = new SoftReference(a); } return a; } }; } /** * Returns a constant function that always uses this value. * * @return A constant function that always uses this value. */ public final F constant() { return new F() { public A f(final B b) { return _1(); } }; } }