SHARE
TWEET

Currying

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