package fj; import fj.data.Array; import fj.data.Either; import fj.data.List; import fj.data.Option; import fj.data.Stream; import fj.data.Validation; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import static fj.P.p; import static fj.Unit.unit; //import fj.data.*; public abstract class P1 implements F0 { @Override public final A f() { return _1(); } /** * Access the first element of the product. * * @return The first element of the product. */ public abstract A _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 P1::_1; } /** * Promote any function to a transformation between P1s. * * @deprecated As of release 4.5, use {@link #map_} * @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 map_(f); } /** * 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> map_(final F f) { return a -> a.map(f); } /** * Binds the given function to the value in a product-1 with a final join. * * @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 final P1 bind(final F> f) { P1 self = this; return P.lazy(() -> f.f(self._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 a -> P.lazy(() -> f.f(a)); } /** * Performs function application within a P1 (applicative functor pattern). * * @param cf The P1 function to apply. * @return A new P1 after applying the given P1 function to the first argument. */ public final P1 apply(final P1> cf) { P1 self = this; return cf.bind(f -> map_(f).f(self)); } /** * Binds the given function to the values in the given P1s with a final join. * * @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 final P1 bind(final P1 cb, final F> f) { return cb.apply(map_(f).f(this)); } /** * Binds the given function to the values in the given P1s with a final join. */ public final P1 bind(final P1 cb, final F2 f) { return bind(cb, F2W.lift(f).curry()); } /** * 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 a.bind(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((pa, pb) -> pa.bind(pb, f)); } public final P1 liftM2(P1 pb, F2 f) { return P.lazy(() -> f.f(_1(), pb._1())); } /** * 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(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 P1::sequence; } /** * 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(Stream.nil())); } /** * Turns an optional P1 into a lazy option. */ public static P1> sequence(final Option> o) { return P.lazy(() -> o.map(P1::_1)); } /** * 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 P.lazy(() -> as.map(P1.__1())); } /** * Traversable instance of P1 for List * * @param f The function that takes A and produces a List (non-deterministic result) * @return A List of P1 */ public final List> traverseList(final F> f){ return f.f(_1()).map(P::p); } /** * Traversable instance of P1 for Either * * @param f The function produces Either * @return An Either of P1 */ public final Either> traverseEither(final F> f){ return f.f(_1()).right().map(P::p); } /** * Traversable instance of P1 for Option * * @param f The function that produces Option * @return An Option of P1 */ public final Option> traverseOption(final F> f){ return f.f(_1()).map(P::p); } /** * Traversable instance of P1 for Validation * * @param f The function might produces Validation * @return An Validation of P1 */ public final Validation> traverseValidation(final F> f){ return f.f(_1()).map(P::p); } /** * Traversable instance of P1 for Stream * * @param f The function that produces Stream * @return An Stream of P1 */ public final Stream> traverseStream(final F> f){ return f.f(_1()).map(P::p); } /** * 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) { final P1 self = this; return P.lazy(() -> f.f(self._1())); } /** * @deprecated since 4.7. Use {@link P1#weakMemo()} instead. */ @Deprecated public final P1 memo() { return weakMemo(); } /** * Returns a P1 that remembers its value. * * @return A P1 that calls this P1 once and remembers the value for subsequent calls. */ public P1 hardMemo() { return new Memo<>(this); } /** * Like memo, but the memoized value is wrapped into a WeakReference */ public P1 weakMemo() { return new WeakReferenceMemo<>(this); } /** * Like memo, but the memoized value is wrapped into a SoftReference */ public P1 softMemo() { return new SoftReferenceMemo<>(this); } /** * @deprecated since 4.7. Use {@link P#weakMemo(F0)} instead. */ @Deprecated public static P1 memo(F f) { return P.weakMemo(() -> f.f(unit())); } /** * @deprecated since 4.7. Use {@link P#weakMemo(F0)} instead. */ @Deprecated public static P1 memo(F0 f) { return P.weakMemo(f); } static final class Memo extends P1 { private volatile F0 fa; private A value; Memo(F0 fa) { this.fa = fa; } @Override public final A _1() { return (fa == null) ? value : computeValue(); } private synchronized A computeValue() { F0 fa = this.fa; if (fa != null) { value = fa.f(); this.fa = null; } return value; } @Override public P1 hardMemo() { return this; } @Override public P1 softMemo() { return this; } @Override public P1 weakMemo() { return this; } } abstract static class ReferenceMemo extends P1 { private final F0 fa; private volatile Reference> v = null; ReferenceMemo(final F0 fa) { this.fa = fa; } @Override public final A _1() { Reference> v = this.v; P1 p1 = v != null ? v.get() : null; return p1 != null ? p1._1() : computeValue(); } private synchronized A computeValue() { Reference> v = this.v; P1 p1 = v != null ? v.get() : null; if (p1 == null) { A a = fa.f(); this.v = newReference(p(a)); return a; } return p1._1(); } abstract Reference newReference(B ref); } static final class WeakReferenceMemo extends ReferenceMemo { WeakReferenceMemo(F0 fa) { super(fa); } @Override Reference newReference(final B ref) { return new WeakReference<>(ref); } @Override public P1 weakMemo() { return this; } } static final class SoftReferenceMemo extends ReferenceMemo { SoftReferenceMemo(F0 self) { super(self); } @Override Reference newReference(final B ref) { return new SoftReference<>(ref); } @Override public P1 softMemo() { return this; } @Override public P1 weakMemo() { return this; } } /** * Returns a constant function that always uses this value. * * @return A constant function that always uses this value. */ public final F constant() { return Function.constant(_1()); } @Override public final String toString() { return Show.p1Show(Show.anyShow()).showS(this); } @Override public final boolean equals(Object other) { return Equal.equals0(P1.class, this, other, () -> Equal.p1Equal(Equal.anyEqual())); } @Override public final int hashCode() { return Hash.p1Hash(Hash.anyHash()).hash(this); } }