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 }