001    package fj;
002    
003    import static fj.FW.$;
004    import static fj.Function.curry;
005    import static fj.Function.uncurryF2;
006    import fj.control.parallel.Promise;
007    import fj.data.Array;
008    import fj.data.IterableW;
009    import fj.data.List;
010    import fj.data.NonEmptyList;
011    import fj.data.Option;
012    import fj.data.Set;
013    import fj.data.Stream;
014    import fj.data.Tree;
015    import fj.data.TreeZipper;
016    import fj.data.Zipper;
017    import static fj.data.TreeZipper.treeZipper;
018    import static fj.data.Zipper.zipper;
019    import static fj.data.IterableW.wrap;
020    import static fj.data.Set.iterableSet;
021    import static fj.data.Tree.node;
022    import fj.pre.Ord;
023    import static fj.P.p;
024    
025    /**
026     * A wrapper for functions of arity 2, that decorates them with higher-order functions.
027     */
028    public final class F2W<A, B, C> implements F2<A, B, C>, F<A, F<B, C>> {
029      private final F2<A, B, C> f;
030    
031      private F2W(final F2<A, B, C> f) {
032        this.f = f;
033      }
034    
035      /**
036       * Returns the undecorated function.
037       *
038       * @return The undecorated function that this wrapper wraps.
039       */
040      public F2<A, B, C> unwrap() {
041        return f;
042      }
043    
044      /**
045       * Function application.
046       *
047       * @param a The <code>A</code> to transform.
048       * @param b The <code>B</code> to transform.
049       * @return The result of the transformation.
050       */
051      public C f(final A a, final B b) {
052        return f.f(a, b);
053      }
054    
055      /**
056       * Partial application.
057       *
058       * @param a The <code>A</code> to which to apply this function.
059       * @return The function partially applied to the given argument.
060       */
061      public FW<B, C> f(final A a) {
062        return $(curry(f).f(a));
063      }
064    
065      /**
066       * Curries this wrapped function to a wrapped function of arity-1 that returns another wrapped function.
067       *
068       * @return a wrapped function of arity-1 that returns another wrapped function.
069       */
070      public FW<A, F<B, C>> curryW() {
071        return $(new F<A, F<B, C>>() {
072          public F<B, C> f(final A a) {
073            return F2W.this.f(a);
074          }
075        });
076      }
077    
078      /**
079       * Wraps a given function, decorating it with higher-order functions.
080       *
081       * @param f The function to wrap.
082       * @return The wrapped function.
083       */
084      public static <A, B, C> F2W<A, B, C> $$(final F2<A, B, C> f) {
085        return new F2W<A, B, C>(f);
086      }
087    
088      /**
089       * Wraps a given function, decorating it with higher-order functions.
090       *
091       * @param f The function to wrap.
092       * @return The wrapped function.
093       */
094      public static <A, B, C> F2W<A, B, C> $$(final F<A, F<B, C>> f) {
095        return $$(uncurryF2(f));
096      }
097    
098      /**
099       * Flips the arguments of this function.
100       *
101       * @return A new function with the arguments of this function flipped.
102       */
103      public F2W<B, A, C> flip() {
104        return $$(Function.flip(f));
105      }
106    
107      /**
108       * Uncurries this function to a function on tuples.
109       *
110       * @return A new function that calls this function with the elements of a given tuple.
111       */
112      public FW<P2<A, B>, C> tuple() {
113        return $(new F<P2<A, B>, C>() {
114          public C f(final P2<A, B> p) {
115            return F2W.this.f(p._1(), p._2());
116          }
117        });
118      }
119    
120      /**
121       * Promotes this function to a function on Arrays.
122       *
123       * @return This function promoted to transform Arrays.
124       */
125      public F2W<Array<A>, Array<B>, Array<C>> array() {
126        return $$(new F2<Array<A>, Array<B>, Array<C>>() {
127          public Array<C> f(final Array<A> a1, final Array<B> a2) {
128            return a2.apply(curryW().mapArray().f(a1));
129          }
130        });
131      }
132    
133      /**
134       * Promotes this function to a function on Promises.
135       *
136       * @return This function promoted to transform Promises.
137       */
138      public F2W<Promise<A>, Promise<B>, Promise<C>> promise() {
139        return $$(Promise.<A, B, C>liftM2(this));
140      }
141    
142      /**
143       * Promotes this function to a function on Iterables.
144       *
145       * @return This function promoted to transform Iterables.
146       */
147      public F2W<Iterable<A>, Iterable<B>, IterableW<C>> iterable() {
148        return $$(IterableW.liftM2(this));
149      }
150    
151      /**
152       * Promotes this function to a function on Lists.
153       *
154       * @return This function promoted to transform Lists.
155       */
156      public F2W<List<A>, List<B>, List<C>> list() {
157        return $$(List.liftM2(this));
158      }
159    
160      /**
161       * Promotes this function to a function on non-empty lists.
162       *
163       * @return This function promoted to transform non-empty lists.
164       */
165      public F2W<NonEmptyList<A>, NonEmptyList<B>, NonEmptyList<C>> nel() {
166        return $$(new F2<NonEmptyList<A>, NonEmptyList<B>, NonEmptyList<C>>() {
167          public NonEmptyList<C> f(final NonEmptyList<A> as, final NonEmptyList<B> bs) {
168            return NonEmptyList.fromList(as.toList().bind(bs.toList(), f)).some();
169          }
170        });
171      }
172    
173      /**
174       * Promotes this function to a function on Options.
175       *
176       * @return This function promoted to transform Options.
177       */
178      public F2W<Option<A>, Option<B>, Option<C>> option() {
179        return $$(Option.liftM2(this));
180      }
181    
182      /**
183       * Promotes this function to a function on Sets.
184       *
185       * @param o An ordering for the result of the promoted function.
186       * @return This function promoted to transform Sets.
187       */
188      public F2W<Set<A>, Set<B>, Set<C>> set(final Ord<C> o) {
189        return $$(new F2<Set<A>, Set<B>, Set<C>>() {
190          public Set<C> f(final Set<A> as, final Set<B> bs) {
191            Set<C> cs = Set.empty(o);
192            for (final A a : as)
193              for (final B b : bs)
194                cs = cs.insert(F2W.this.f(a, b));
195            return cs;
196          }
197        });
198      }
199    
200      /**
201       * Promotes this function to a function on Streams.
202       *
203       * @return This function promoted to transform Streams.
204       */
205      public F2W<Stream<A>, Stream<B>, Stream<C>> stream() {
206        return $$(new F2<Stream<A>, Stream<B>, Stream<C>>() {
207          public Stream<C> f(final Stream<A> as, final Stream<B> bs) {
208            return as.bind(bs, f);
209          }
210        });
211      }
212    
213      /**
214       * Promotes this function to a function on Trees.
215       *
216       * @return This function promoted to transform Trees.
217       */
218      public F2W<Tree<A>, Tree<B>, Tree<C>> tree() {
219        return $$(new F2<Tree<A>, Tree<B>, Tree<C>>() {
220          public Tree<C> f(final Tree<A> as, final Tree<B> bs) {
221            final F2<Tree<A>, Tree<B>, Tree<C>> self = this;
222            return node(F2W.this.f(as.root(), bs.root()), new P1<Stream<Tree<C>>>() {
223              public Stream<Tree<C>> _1() {
224                return $$(self).stream().f(as.subForest()._1(), bs.subForest()._1());
225              }
226            });
227          }
228        });
229      }
230    
231      /**
232       * Promotes this function to zip two arrays, applying the function lock-step over both Arrays.
233       *
234       * @return A function that zips two arrays with this function.
235       */
236      public F2W<Array<A>, Array<B>, Array<C>> zipArray() {
237        return $$(new F2<Array<A>, Array<B>, Array<C>>() {
238          public Array<C> f(final Array<A> as, final Array<B> bs) {
239            return as.zipWith(bs, f);
240          }
241        });
242      }
243    
244      /**
245       * Promotes this function to zip two iterables, applying the function lock-step over both iterables.
246       *
247       * @return A function that zips two iterables with this function.
248       */
249      public F2W<Iterable<A>, Iterable<B>, Iterable<C>> zipIterable() {
250        return $$(new F2<Iterable<A>, Iterable<B>, Iterable<C>>() {
251          public Iterable<C> f(final Iterable<A> as, final Iterable<B> bs) {
252            return wrap(as).zipWith(bs, f);
253          }
254        });
255      }
256    
257      /**
258       * Promotes this function to zip two lists, applying the function lock-step over both lists.
259       *
260       * @return A function that zips two lists with this function.
261       */
262      public F2W<List<A>, List<B>, List<C>> zipList() {
263        return $$(new F2<List<A>, List<B>, List<C>>() {
264          public List<C> f(final List<A> as, final List<B> bs) {
265            return as.zipWith(bs, f);
266          }
267        });
268      }
269    
270    
271      /**
272       * Promotes this function to zip two streams, applying the function lock-step over both streams.
273       *
274       * @return A function that zips two streams with this function.
275       */
276      public F2W<Stream<A>, Stream<B>, Stream<C>> zipStream() {
277        return $$(new F2<Stream<A>, Stream<B>, Stream<C>>() {
278          public Stream<C> f(final Stream<A> as, final Stream<B> bs) {
279            return as.zipWith(bs, f);
280          }
281        });
282      }
283    
284      /**
285       * Promotes this function to zip two non-empty lists, applying the function lock-step over both lists.
286       *
287       * @return A function that zips two non-empty lists with this function.
288       */
289      public F2W<NonEmptyList<A>, NonEmptyList<B>, NonEmptyList<C>> zipNel() {
290        return $$(new F2<NonEmptyList<A>, NonEmptyList<B>, NonEmptyList<C>>() {
291          public NonEmptyList<C> f(final NonEmptyList<A> as, final NonEmptyList<B> bs) {
292            return NonEmptyList.fromList(as.toList().zipWith(bs.toList(), f)).some();
293          }
294        });
295      }
296    
297      /**
298       * Promotes this function to zip two sets, applying the function lock-step over both sets.
299       *
300       * @param o An ordering for the resulting set.
301       * @return A function that zips two sets with this function.
302       */
303      public F2W<Set<A>, Set<B>, Set<C>> zipSet(final Ord<C> o) {
304        return $$(new F2<Set<A>, Set<B>, Set<C>>() {
305          public Set<C> f(final Set<A> as, final Set<B> bs) {
306            return iterableSet(o, as.toStream().zipWith(bs.toStream(), f));
307          }
308        });
309      }
310    
311      /**
312       * Promotes this function to zip two trees, applying the function lock-step over both trees.
313       * The structure of the resulting tree is the structural intersection of the two trees.
314       *
315       * @return A function that zips two trees with this function.
316       */
317      public F2W<Tree<A>, Tree<B>, Tree<C>> zipTree() {
318        return $$(new F2<Tree<A>, Tree<B>, Tree<C>>() {
319          public Tree<C> f(final Tree<A> ta, final Tree<B> tb) {
320            final F2<Tree<A>, Tree<B>, Tree<C>> self = this;
321            return node(F2W.this.f(ta.root(), tb.root()), new P1<Stream<Tree<C>>>() {
322              public Stream<Tree<C>> _1() {
323                return $$(self).zipStream().f(ta.subForest()._1(), tb.subForest()._1());
324              }
325            });
326          }
327        });
328      }
329    
330      /**
331       * Promotes this function to zip two zippers, applying the function lock-step over both zippers in both directions.
332       * The structure of the resulting zipper is the structural intersection of the two zippers.
333       *
334       * @return A function that zips two zippers with this function.
335       */
336      public F2W<Zipper<A>, Zipper<B>, Zipper<C>> zipZipper() {
337        return $$(new F2<Zipper<A>, Zipper<B>, Zipper<C>>() {
338          public Zipper<C> f(final Zipper<A> ta, final Zipper<B> tb) {
339            final F2W<Stream<A>, Stream<B>, Stream<C>> sf = $$(f).zipStream();
340            return zipper(sf.f(ta.lefts(), tb.lefts()), f.f(ta.focus(), tb.focus()), sf.f(ta.rights(), tb.rights()));
341          }
342        });
343      }
344    
345      /**
346       * Promotes this function to zip two TreeZippers, applying the function lock-step over both zippers in all directions.
347       * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers.
348       *
349       * @return A function that zips two TreeZippers with this function.
350       */
351      public F2W<TreeZipper<A>, TreeZipper<B>, TreeZipper<C>> zipTreeZipper() {
352        return $$(new F2<TreeZipper<A>, TreeZipper<B>, TreeZipper<C>>() {
353          public TreeZipper<C> f(final TreeZipper<A> ta, final TreeZipper<B> tb) {
354            final F2<Stream<Tree<A>>, Stream<Tree<B>>, Stream<Tree<C>>> sf = $$(f).tree().zipStream();
355            final
356            F2<Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>>,
357                Stream<P3<Stream<Tree<B>>, B, Stream<Tree<B>>>>,
358                Stream<P3<Stream<Tree<C>>, C, Stream<Tree<C>>>>>
359                pf =
360                $$(new F2<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>,
361                    P3<Stream<Tree<B>>, B, Stream<Tree<B>>>,
362                    P3<Stream<Tree<C>>, C, Stream<Tree<C>>>>() {
363                  public P3<Stream<Tree<C>>, C, Stream<Tree<C>>> f(final P3<Stream<Tree<A>>, A, Stream<Tree<A>>> pa,
364                                                                   final P3<Stream<Tree<B>>, B, Stream<Tree<B>>> pb) {
365                    return p(tree().zipStream().f(pa._1(), pb._1()), f.f(pa._2(), pb._2()),
366                             tree().zipStream().f(pa._3(), pb._3()));
367                  }
368                }).zipStream();
369            return treeZipper(tree().f(ta.p()._1(), tb.p()._1()), sf.f(ta.lefts(), tb.lefts()),
370                              sf.f(ta.rights(), tb.rights()), pf.f(ta.p()._4(), tb.p()._4()));
371          }
372        });
373      }
374    }