package fj; import fj.control.parallel.Promise; import fj.data.Array; import fj.data.IterableW; import fj.data.List; import fj.data.NonEmptyList; import fj.data.Option; import fj.data.Set; import fj.data.Stream; import fj.data.Tree; import fj.data.TreeZipper; import fj.data.Zipper; import static fj.data.Tree.node; import static fj.P.p; import static fj.data.IterableW.wrap; import static fj.data.Set.iterableSet; import static fj.data.TreeZipper.treeZipper; import static fj.data.Zipper.zipper; /** * A transformation function of arity-2 from A and B to C. * This type can be represented using the Java 7 closure syntax. * * @version %build.number% */ public abstract class F2 { /** * Transform A and B to C. * * @param a The A to transform. * @param b The B to transform. * @return The result of the transformation. */ public abstract C f(A a, B b); /** * Partial application. * * @param a The A to which to apply this function. * @return The function partially applied to the given argument. */ public final F f(final A a) { return new F() { public C f(final B b) { return F2.this.f(a, b); } }; } /** * Curries this wrapped function to a wrapped function of arity-1 that returns another wrapped function. * * @return a wrapped function of arity-1 that returns another wrapped function. */ public final F> curry() { return new F>() { public F f(final A a) { return new F() { public C f(final B b) { return F2.this.f(a, b); } }; } }; } /** * Flips the arguments of this function. * * @return A new function with the arguments of this function flipped. */ public final F2 flip() { return new F2() { public C f(final B b, final A a) { return F2.this.f(a, b); } }; } /** * Uncurries this function to a function on tuples. * * @return A new function that calls this function with the elements of a given tuple. */ public final F, C> tuple() { return new F, C>() { public C f(final P2 p) { return F2.this.f(p._1(), p._2()); } }; } /** * Promotes this function to a function on Arrays. * * @return This function promoted to transform Arrays. */ public final F2, Array, Array> arrayM() { return new F2, Array, Array>() { public Array f(final Array a, final Array b) { return a.bind(b, F2.this.curry()); } }; } /** * Promotes this function to a function on Promises. * * @return This function promoted to transform Promises. */ public final F2, Promise, Promise> promiseM() { return new F2, Promise, Promise>() { public Promise f(final Promise a, final Promise b) { return a.bind(b, F2.this.curry()); } }; } /** * Promotes this function to a function on Iterables. * * @return This function promoted to transform Iterables. */ public final F2, Iterable, IterableW> iterableM() { return new F2, Iterable, IterableW>() { public IterableW f(final Iterable a, final Iterable b) { return IterableW.liftM2(F2.this.curry()).f(a).f(b); } }; } /** * Promotes this function to a function on Lists. * * @return This function promoted to transform Lists. */ public final F2, List, List> listM() { return new F2, List, List>() { public List f(final List a, final List b) { return List.liftM2(F2.this.curry()).f(a).f(b); } }; } /** * Promotes this function to a function on non-empty lists. * * @return This function promoted to transform non-empty lists. */ public final F2, NonEmptyList, NonEmptyList> nelM() { return new F2, NonEmptyList, NonEmptyList>() { public NonEmptyList f(final NonEmptyList as, final NonEmptyList bs) { return NonEmptyList.fromList(as.toList().bind(bs.toList(), F2.this)).some(); } }; } /** * Promotes this function to a function on Options. * * @return This function promoted to transform Options. */ public final F2, Option, Option> optionM() { return new F2, Option, Option>() { public Option f(final Option a, final Option b) { return Option.liftM2(F2.this.curry()).f(a).f(b); } }; } /** * Promotes this function to a function on Sets. * * @param o An ordering for the result of the promoted function. * @return This function promoted to transform Sets. */ public final F2, Set, Set> setM(final Ord o) { return new F2, Set, Set>() { public Set f(final Set as, final Set bs) { Set cs = Set.empty(o); for (final A a : as) for (final B b : bs) cs = cs.insert(F2.this.f(a, b)); return cs; } }; } /** * Promotes this function to a function on Streams. * * @return This function promoted to transform Streams. */ public final F2, Stream, Stream> streamM() { return new F2, Stream, Stream>() { public Stream f(final Stream as, final Stream bs) { return as.bind(bs, F2.this); } }; } /** * Promotes this function to a function on Trees. * * @return This function promoted to transform Trees. */ public final F2, Tree, Tree> treeM() { return new F2, Tree, Tree>() { public Tree f(final Tree as, final Tree bs) { final F2, Tree, Tree> self = this; return node(F2.this.f(as.root(), bs.root()), new P1>>() { public Stream> _1() { return self.streamM().f(as.subForest()._1(), bs.subForest()._1()); } }); } }; } /** * Promotes this function to zip two arrays, applying the function lock-step over both Arrays. * * @return A function that zips two arrays with this function. */ public final F2, Array, Array> zipArrayM() { return new F2, Array, Array>() { public Array f(final Array as, final Array bs) { return as.zipWith(bs, F2.this); } }; } /** * Promotes this function to zip two iterables, applying the function lock-step over both iterables. * * @return A function that zips two iterables with this function. */ public final F2, Iterable, Iterable> zipIterableM() { return new F2, Iterable, Iterable>() { public Iterable f(final Iterable as, final Iterable bs) { return wrap(as).zipWith(bs, F2.this); } }; } /** * Promotes this function to zip two lists, applying the function lock-step over both lists. * * @return A function that zips two lists with this function. */ public final F2, List, List> zipListM() { return new F2, List, List>() { public List f(final List as, final List bs) { return as.zipWith(bs, F2.this); } }; } /** * Promotes this function to zip two streams, applying the function lock-step over both streams. * * @return A function that zips two streams with this function. */ public final F2, Stream, Stream> zipStreamM() { return new F2, Stream, Stream>() { public Stream f(final Stream as, final Stream bs) { return as.zipWith(bs, F2.this); } }; } /** * Promotes this function to zip two non-empty lists, applying the function lock-step over both lists. * * @return A function that zips two non-empty lists with this function. */ public final F2, NonEmptyList, NonEmptyList> zipNelM() { return new F2, NonEmptyList, NonEmptyList>() { public NonEmptyList f(final NonEmptyList as, final NonEmptyList bs) { return NonEmptyList.fromList(as.toList().zipWith(bs.toList(), F2.this)).some(); } }; } /** * Promotes this function to zip two sets, applying the function lock-step over both sets. * * @param o An ordering for the resulting set. * @return A function that zips two sets with this function. */ public final F2, Set, Set> zipSetM(final Ord o) { return new F2, Set, Set>() { public Set f(final Set as, final Set bs) { return iterableSet(o, as.toStream().zipWith(bs.toStream(), F2.this)); } }; } /** * Promotes this function to zip two trees, applying the function lock-step over both trees. * The structure of the resulting tree is the structural intersection of the two trees. * * @return A function that zips two trees with this function. */ public final F2, Tree, Tree> zipTreeM() { return new F2, Tree, Tree>() { public Tree f(final Tree ta, final Tree tb) { final F2, Tree, Tree> self = this; return node(F2.this.f(ta.root(), tb.root()), new P1>>() { public Stream> _1() { return self.zipStreamM().f(ta.subForest()._1(), tb.subForest()._1()); } }); } }; } /** * Promotes this function to zip two zippers, applying the function lock-step over both zippers in both directions. * The structure of the resulting zipper is the structural intersection of the two zippers. * * @return A function that zips two zippers with this function. */ public final F2, Zipper, Zipper> zipZipperM() { return new F2, Zipper, Zipper>() { @SuppressWarnings({"unchecked"}) public Zipper f(final Zipper ta, final Zipper tb) { final F2, Stream, Stream> sf = F2.this.zipStreamM(); return zipper(sf.f(ta.lefts(), tb.lefts()), F2.this.f(ta.focus(), tb.focus()), sf.f(ta.rights(), tb.rights())); } }; } /** * Promotes this function to zip two TreeZippers, applying the function lock-step over both zippers in all directions. * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. * * @return A function that zips two TreeZippers with this function. */ public final F2, TreeZipper, TreeZipper> zipTreeZipperM() { return new F2, TreeZipper, TreeZipper>() { @SuppressWarnings({"unchecked"}) public TreeZipper f(final TreeZipper ta, final TreeZipper tb) { final F2>, Stream>, Stream>> sf = F2.this.treeM().zipStreamM(); final F2>, A, Stream>>>, Stream>, B, Stream>>>, Stream>, C, Stream>>>> pf = new F2>, A, Stream>>, P3>, B, Stream>>, P3>, C, Stream>>>() { public P3>, C, Stream>> f(final P3>, A, Stream>> pa, final P3>, B, Stream>> pb) { return p(F2.this.treeM().zipStreamM().f(pa._1(), pb._1()), F2.this.f(pa._2(), pb._2()), F2.this.treeM().zipStreamM().f(pa._3(), pb._3())); } }.zipStreamM(); return treeZipper(F2.this.treeM().f(ta.p()._1(), tb.p()._1()), sf.f(ta.lefts(), tb.lefts()), sf.f(ta.rights(), tb.rights()), pf.f(ta.p()._4(), tb.p()._4())); } }; } }