package ch.claude_martin.java8;
import java.util.*;
import java.util.function.Function;
import java.util.stream.*;
/**
* Currying examples.
*
* @author Claude Martin
*
*/
public class Currying {
/** A 2-Tuple with two values. */
static class Pair {
public Pair(final A first, final B second) {
super();
this._1 = first;
this._2 = second;
}
final A _1;
final B _2;
}
/** A 3-Tuple with three values. */
static class Triple extends Pair {
public Triple(final A first, final B second, final C third) {
super(first, second);
this._3 = third;
}
final C _3;
}
/** A 4-Tuple with four values. */
static class Quad extends Triple {
public Quad(final A first, final B second, final C third, final D fourth) {
super(first, second, third);
this._4 = fourth;
}
final D _4;
}
/**
* Run this to see what it does. I recommend using a debugger for this.
*/
public static void main(final String... args) {
{
// A function always consumes only one parameter!
// If you want more parameters you must return a function to consume more
// parameters.
// This is the "add"-function (addition):
final Function> add = i -> j -> i + j;
Integer result = add.apply(1).apply(2); // = apply(add, 1, 2);
System.out.println(result);// =3
// But you can just claim that the two parameters are really just one.
// Just create a Pair and pass it as one parameter:
final Function, Integer> add2 = p -> add.apply(p._1).apply(p._2);
result = add2.apply(new Pair<>(1, 2));
System.out.println(result);// =3
// Each function that consumes just one pair can be converted to a
// function that returns a function:
final Function> add3 = i -> j -> add2.apply(new Pair<>(i, j));
result = apply(add3, 1, 2);
System.out.println(result);// =3
// curry2 is a function that "curries" a function that takes a pair:
result = curry2(add2).apply(1).apply(2);
System.out.println(result);// =3
// curry2(uncurry2(x)) = x
result = curry2(uncurry2(curry2(uncurry2(curry2(uncurry2(curry2(add2))))))).apply(1).apply(2);
System.out.println(result);// =3
// uncurry does the opposite:
result = uncurry2(add).apply(new Pair<>(1, 2));
System.out.println(result);// =3
// The same is true for more than two parameter:
final Function>> f = a -> b -> c -> a * b + c;
result = uncurry3(f).apply(new Triple<>(1, 2, 3));
System.out.println(result);
result = apply(f, 1, 2, 3);
System.out.println(result);// =5
// You can only apply some parameters and get a function, then you can
// later apply the rest.
// partial application:
Function part = apply(f, 1, 2);
System.out.println(part); // ...$$Lambda$...
// apply last argument:
result = apply(part, 3);
System.out.println(result); // =5
// applyR does exactly the same as apply, but it is recursive.
part = applyR(f, 1); // only one argument this time
System.out.println(part); // ...$$Lambda$...
// apply last two arguments:
result = applyR(part, 2, 3);
System.out.println(result); // =5
// Some functions even consume a 4-Tuple!
final Function, Integer> f2 = q -> q._1 * q._2.length() + (q._3 ? q._4 : 0);
result = apply(curry4(f2), 1, "hallo", true, 4);
System.out.println(result);// =9
result = curry4(f2).apply(1).apply("hallo").apply(true).apply(4);
System.out.println(result);// =9
}
{ // Some fun with Collections:
final Collection list = Arrays.asList(1, 2, 3, 4);
final Function> add = i -> j -> i + j;
// partially apply all integers to add:
final Stream> list2 = list.stream().map(add);
// Now apply 42 as the "second" parameter:
final Stream list3 = list2.map(f2 -> f2.apply(42));
// Print to console:
System.out.println(Arrays.toString(list3.toArray())); // = [43, 44, 45, 46]
}
{ // More fun with Collections:
final Collection listA = Arrays.asList(1, 2, 3, 4);
final Collection listB = Arrays.asList(5, 6, 7, 8);
final Function> add = i -> j -> i + j;
// Where did Stream::zip go?! Not available in this beta release...
// listA.stream().zip(listB.stream()).map(...
final Collection> zip = new LinkedList<>();
final Iterator iteratorA = listA.iterator();
final Iterator iteratorB = listB.iterator();
while (iteratorA.hasNext() && iteratorB.hasNext())
zip.add(new Pair<>(iteratorA.next(), iteratorB.next()));
final List results = zip.stream().map(uncurry2(add)).collect(Collectors. toList());
System.out.println(results.toString()); // = [6, 8, 10, 12]
}
}
/**
* Applies the first argument to the function and then the next to the
* returned function. The application of all given parameters must result in a
* complete application of the given function.
*
* @param f
* Curried Function.
* @param arg0
* The first argument is type checked.
* @param args
* More arguments.
* @return Returns the value of the last function.
* @throws IllegalArgumentException
* Thrown when the arguments don't match.
* @throws ClassCastException
* The return value will be cast implicitly, which could cause a
* ClassCastException.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
static T apply(final Function super X, ?> f, final X arg0, final Object... args) throws ClassCastException, IllegalArgumentException {
Objects.requireNonNull(f, "The function argument must not be null.");
Object result = f.apply(arg0);
if (args != null && args.length > 0) {
Function f2 = requireFunction(result);
for (int i = 0; i < args.length - 1; i++)
f2 = requireFunction(f2.apply(args[i]));
result = f2.apply(args[args.length - 1]);
}
return (T) result;
}
/** Cast to function or throw IllegalArgumentException. */
@SuppressWarnings("rawtypes")
private static Function requireFunction(final Object x) {
if (!(x instanceof Function))
throw new IllegalArgumentException("Result was not a function, but more arguments remain.");
return (Function) x;
}
/** The same as {@link #apply(Function, Object, Object...)}, but recursive. */
@SafeVarargs
@SuppressWarnings("unchecked")
static T applyR(final Function super X, ?> f, final X arg0, final Y... args) throws ClassCastException, IllegalArgumentException {
if (args == null || args.length == 0)
return (T) f.apply(arg0);
else
return (T) applyR(requireFunction(f.apply(arg0)), args[0], Arrays.copyOfRange(args, 1, args.length));
}
/** Converts an uncurried function to a curried function. */
static Function> curry2(final Function, C> f) {
return a -> b -> f.apply(new Pair<>(a, b));
}
/** Converts a curried function to a function on pairs. */
static Function, C> uncurry2(final Function> f) {
return (Pair p) -> f.apply(p._1).apply(p._2);
}
/** Converts an uncurried function to a curried function. */
static Function>> curry3(final Function, D> f) {
return a -> b -> c -> f.apply(new Triple<>(a, b, c));
}
/** Converts a curried function to a function on triples. */
static Function, D> uncurry3(final Function>> f) {
return (Triple p) -> f.apply(p._1).apply(p._2).apply(p._3);
}
/** Converts an uncurried function to a curried function. */
static Function>>> curry4(final Function, E> f) {
return a -> b -> c -> d -> f.apply(new Quad<>(a, b, c, d));
}
/** Converts a curried function to a function on quads. */
static Function, E> uncurry4(final Function>>> f) {
return (Quad p) -> f.apply(p._1).apply(p._2).apply(p._3).apply(p._4);
}
}