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 }