Advertisement
DulcetAirman

Java 8 Currying

Oct 21st, 2013
317
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.93 KB | None | 0 0
  1. package ch.claude_martin.java8;
  2.  
  3. import java.util.Arrays;
  4. import java.util.Collection;
  5. import java.util.Iterator;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.Objects;
  9. import java.util.function.Function;
  10. import java.util.stream.Collectors;
  11. import java.util.stream.Stream;
  12.  
  13. /**
  14.  * Currying is great to spice up your code!
  15.  * <p>
  16.  * Java isn't as elegant as Haskell when it comes to Currying, but here are some
  17.  * examples.
  18.  * <p>
  19.  * Read the <a
  20.  * href="http://www.haskell.org/haskellwiki/Currying">HaskellWiki</a> for more
  21.  * info.
  22.  * <p>
  23.  * This was made with JDK8 Beta 2013-oct-10 (1.8.0-ea-b111). Also tested with
  24.  * the newer Beta 120.
  25.  *
  26.  * @author Claude Martin
  27.  *
  28.  */
  29. public class Currying {
  30.     /** A 2-Tuple with two values. */
  31.     static class Pair<A, B> {
  32.         public Pair(final A first, final B second) {
  33.             super();
  34.             this._1 = first;
  35.             this._2 = second;
  36.         }
  37.  
  38.         final A _1;
  39.         final B _2;
  40.     }
  41.  
  42.     /** A 3-Tuple with three values. */
  43.     static class Triple<A, B, C> extends Pair<A, B> {
  44.         public Triple(final A first, final B second, final C third) {
  45.             super(first, second);
  46.             this._3 = third;
  47.         }
  48.  
  49.         final C _3;
  50.     }
  51.  
  52.     /** A 4-Tuple with four values. */
  53.     static class Quad<A, B, C, D> extends Triple<A, B, C> {
  54.         public Quad(final A first, final B second, final C third, final D fourth) {
  55.             super(first, second, third);
  56.             this._4 = fourth;
  57.         }
  58.  
  59.         final D _4;
  60.     }
  61.  
  62.     /**
  63.      * Run this to see what it does. I recommend using a debugger for this.
  64.      */
  65.     public static void main(final String... args) {
  66.         {
  67.             // A function always consumes only one parameter!
  68.             // If you want more parameters you must return a function to consume more
  69.             // parameters.
  70.             // This is the "add"-function (addition):
  71.             final Function<Integer, Function<Integer, Integer>> add = i -> j -> i + j;
  72.             Integer result = add.apply(1).apply(2); // = apply(add, 1, 2);
  73.             System.out.println(result);// =3
  74.  
  75.             // But you can just claim that the two parameters are really just one.
  76.             // Just create a Pair and pass it as one parameter:
  77.             final Function<Pair<Integer, Integer>, Integer> add2 = p -> add.apply(p._1).apply(p._2);
  78.             result = add2.apply(new Pair<>(1, 2));
  79.             System.out.println(result);// =3
  80.  
  81.             // Each function that consumes just one pair can be converted to a
  82.             // function that returns a function:
  83.             final Function<Integer, Function<Integer, Integer>> add3 = i -> j -> add2.apply(new Pair<>(i, j));
  84.             result = apply(add3, 1, 2);
  85.             System.out.println(result);// =3
  86.  
  87.             // curry2 is a function that "curries" a function that takes a pair:
  88.             result = curry2(add2).apply(1).apply(2);
  89.             System.out.println(result);// =3
  90.  
  91.             // curry2(uncurry2(x)) = x
  92.             result = curry2(uncurry2(curry2(uncurry2(curry2(uncurry2(curry2(add2))))))).apply(1).apply(2);
  93.             System.out.println(result);// =3
  94.  
  95.             // uncurry does the opposite:
  96.             result = uncurry2(add).apply(new Pair<>(1, 2));
  97.             System.out.println(result);// =3
  98.  
  99.             // The same is true for more than two parameter:
  100.             final Function<Integer, Function<Integer, Function<Integer, Integer>>> f = a -> b -> c -> a * b + c;
  101.             result = uncurry3(f).apply(new Triple<>(1, 2, 3));
  102.             System.out.println(result);
  103.             result = apply(f, 1, 2, 3);
  104.             System.out.println(result);// =5
  105.  
  106.             // You can only apply some parameters and get a function, then you can
  107.             // later apply the rest.
  108.             // partial application:
  109.             Function<Integer, Integer> part = apply(f, 1, 2);
  110.             System.out.println(part); // ...$$Lambda$...
  111.             // apply last argument:
  112.             result = apply(part, 3);
  113.             System.out.println(result); // =5
  114.  
  115.             // applyR does exactly the same as apply, but it is recursive.
  116.             part = applyR(f, 1); // only one argument this time
  117.             System.out.println(part); // ...$$Lambda$...
  118.             // apply last two arguments:
  119.             result = applyR(part, 2, 3);
  120.             System.out.println(result); // =5
  121.  
  122.             // Some functions even consume a 4-Tuple!
  123.             final Function<Quad<Integer, String, Boolean, Integer>, Integer> f2 = q -> q._1 * q._2.length() + (q._3 ? q._4 : 0);
  124.             result = apply(curry4(f2), 1, "hallo", true, 4);
  125.             System.out.println(result);// =9
  126.             result = curry4(f2).apply(1).apply("hallo").apply(true).apply(4);
  127.             System.out.println(result);// =9
  128.         }
  129.  
  130.         { // Some fun with Collections:
  131.             final Collection<Integer> list = Arrays.asList(1, 2, 3, 4);
  132.             final Function<Integer, Function<Integer, Integer>> add = i -> j -> i + j;
  133.             // partially apply all integers to add:
  134.             final Stream<Function<Integer, Integer>> list2 = list.stream().map(add);
  135.             // Now apply 42 as the "second" parameter:
  136.             final Stream<Integer> list3 = list2.map(f2 -> f2.apply(42));
  137.             // Print to console:
  138.             System.out.println(Arrays.toString(list3.toArray())); // = [43, 44, 45, 46]
  139.         }
  140.  
  141.         { // More fun with Collections:
  142.             final Collection<Integer> listA = Arrays.asList(1, 2, 3, 4);
  143.             final Collection<Integer> listB = Arrays.asList(5, 6, 7, 8);
  144.             final Function<Integer, Function<Integer, Integer>> add = i -> j -> i + j;
  145.  
  146.             // Where did Stream::zip go?! Not available in this beta release...
  147.             // listA.stream().zip(listB.stream()).map(...
  148.  
  149.             final Collection<Pair<Integer, Integer>> zip = new LinkedList<>();
  150.             final Iterator<Integer> iteratorA = listA.iterator();
  151.             final Iterator<Integer> iteratorB = listB.iterator();
  152.             while (iteratorA.hasNext() && iteratorB.hasNext())
  153.                 zip.add(new Pair<>(iteratorA.next(), iteratorB.next()));
  154.  
  155.             final List<Integer> results = zip.stream().map(uncurry2(add)).collect(Collectors.<Integer> toList());
  156.             System.out.println(results.toString()); // = [6, 8, 10, 12]
  157.         }
  158.     }
  159.  
  160.     /**
  161.      * Applies the first argument to the function and then the next to the
  162.      * returned function. The application of all given parameters must result in a
  163.      * complete application of the given function.
  164.      *
  165.      * @param f
  166.      *          Curried Function.
  167.      * @param arg0
  168.      *          The first argument is type checked.
  169.      * @param args
  170.      *          More arguments.
  171.      * @return Returns the value of the last function.
  172.      * @throws IllegalArgumentException
  173.      *           Thrown when the arguments don't match.
  174.      * @throws ClassCastException
  175.      *           The return value will be cast implicitly, which could cause a
  176.      *           ClassCastException.
  177.      */
  178.     @SuppressWarnings({ "unchecked", "rawtypes" })
  179.     static <T, X> T apply(final Function<? super X, ?> f, final X arg0, final Object... args) throws ClassCastException, IllegalArgumentException {
  180.         Objects.requireNonNull(f, "The function argument must not be null.");
  181.         Object result = f.apply(arg0);
  182.         if (args != null && args.length > 0) {
  183.             Function f2 = requireFunction(result);
  184.             for (int i = 0; i < args.length - 1; i++)
  185.                 f2 = requireFunction(f2.apply(args[i]));
  186.             result = f2.apply(args[args.length - 1]);
  187.         }
  188.         return (T) result;
  189.     }
  190.  
  191.     /** Cast to function or throw IllegalArgumentException. */
  192.     @SuppressWarnings("rawtypes")
  193.     private static Function requireFunction(final Object x) {
  194.         if (!(x instanceof Function))
  195.             throw new IllegalArgumentException("Result was not a function, but more arguments remain.");
  196.         return (Function) x;
  197.     }
  198.  
  199.     /** The same as {@link #apply(Function, Object, Object...)}, but recursive. */
  200.     @SafeVarargs
  201.     @SuppressWarnings("unchecked")
  202.     static <T, X, Y> T applyR(final Function<? super X, ?> f, final X arg0, final Y... args) throws ClassCastException, IllegalArgumentException {
  203.         if (args == null || args.length == 0)
  204.             return (T) f.apply(arg0);
  205.         else
  206.             return (T) applyR(requireFunction(f.apply(arg0)), args[0], Arrays.copyOfRange(args, 1, args.length));
  207.     }
  208.  
  209.     /** Converts an uncurried function to a curried function. */
  210.     static <A, B, C> Function<A, Function<B, C>> curry2(final Function<Pair<A, B>, C> f) {
  211.         return a -> b -> f.apply(new Pair<>(a, b));
  212.     }
  213.  
  214.     /** Converts a curried function to a function on pairs. */
  215.     static <A, B, C> Function<Pair<A, B>, C> uncurry2(final Function<A, Function<B, C>> f) {
  216.         return (Pair<A, B> p) -> f.apply(p._1).apply(p._2);
  217.     }
  218.  
  219.     /** Converts an uncurried function to a curried function. */
  220.     static <A, B, C, D> Function<A, Function<B, Function<C, D>>> curry3(final Function<Triple<A, B, C>, D> f) {
  221.         return a -> b -> c -> f.apply(new Triple<>(a, b, c));
  222.     }
  223.  
  224.     /** Converts a curried function to a function on triples. */
  225.     static <A, B, C, D> Function<Triple<A, B, C>, D> uncurry3(final Function<A, Function<B, Function<C, D>>> f) {
  226.         return (Triple<A, B, C> p) -> f.apply(p._1).apply(p._2).apply(p._3);
  227.     }
  228.  
  229.     /** Converts an uncurried function to a curried function. */
  230.     static <A, B, C, D, E> Function<A, Function<B, Function<C, Function<D, E>>>> curry4(final Function<Quad<A, B, C, D>, E> f) {
  231.         return a -> b -> c -> d -> f.apply(new Quad<>(a, b, c, d));
  232.     }
  233.  
  234.     /** Converts a curried function to a function on quads. */
  235.     static <A, B, C, D, E> Function<Quad<A, B, C, D>, E> uncurry4(final Function<A, Function<B, Function<C, Function<D, E>>>> f) {
  236.         return (Quad<A, B, C, D> p) -> f.apply(p._1).apply(p._2).apply(p._3).apply(p._4);
  237.     }
  238.  
  239. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement