123

I have read several Java 8 tutorials before.

Right now I encountered following topic: Does java support Currying?

Here, I see following code:

IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));

I understand that this example sum 2 elements but I cannot understand the construction:

a -> b -> a + b;

According to the left part of expression, this row should implement following function:

R apply(int value); 

Before this, I only met lambdas only with one arrow.

2
  • 6
    Do you understand what currying means? That's pretty integral to this question. Commented Sep 28, 2015 at 23:06
  • 12
    It's just a lambda that returns a lambda. Commented Sep 28, 2015 at 23:13

7 Answers 7

120

If you express this as non-shorthand lambda syntax or pre-lambda Java anonymous class syntax it is clearer what is happening...

The original question. Why are two arrows? Simple, there are two functions being defined... The first function is a function-defining-function, the second is the result of that function, which also happens to be function. Each requires an -> operator to define it.

Non-shorthand

IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
    return (b) -> {
        return a + b;
    };
};

Pre-Lambda before Java 8

IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(final int value) {
        IntUnaryOperator op = new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand + value;
            }
        };
        return op;
    }
};
Sign up to request clarification or add additional context in comments.

6 Comments

pre-lambda requires a final int value
Yes, you're correct, but I wrote that still using a Java 8 compiler which allows you to use things that are "effectively final"
@gstackoverflow yes, but then java 8 is not pre-lambda
you can use pre-lambda style in java 8 too. I wrote it JFY
Wasn't lambdas done just so SO folks could score points ? :-)
|
49

An IntFunction<R> is a function int -> R. An IntUnaryOperator is a function int -> int.

Thus an IntFunction<IntUnaryOperator> is a function that takes an int as parameter and return a function that takes an int as parameter and return an int.

a -> b -> a + b;
^    |         |
|     ---------
|         ^
|         |
|         The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction

Maybe it is more clear if you use anonymous classes to "decompose" the lambda:

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};

Comments

30

Adding parentheses may make this more clear:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Or probably intermediate variable may help:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};

Comments

24

Let's rewrite that lambda expression with parentheses to make it more clear:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

So we are declaring a function taking an int which returns a Function. More specifically, the function returned takes an int and returns an int (the sum of the two elements): this can be represented as an IntUnaryOperator.

Therefore, curriedAdd is a function taking an int and returning an IntUnaryOperator, so it can be represented as IntFunction<IntUnaryOperator>.

Comments

10

It's two lambda expressions.

IntFunction<IntUnaryOperator> curriedAdd = 
  a -> { //this is for the fixed value
    return b -> { //this is for the add operation
      return a + b;
    };
  }

IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14

Comments

8

If you look at IntFunction it might become clearer: IntFunction<R> is a FunctionalInterface. It represents a function that takes an int and returns a value of type R.

In this case, the return type R is also a FunctionalInterface, namely an IntUnaryOperator. So the first (outer) function itself returns a function.

In this case: When applied to an int, curriedAdd is supposed to return a function that again takes an int (and returns again int, because that's what IntUnaryOperator does).

In functional programming it is common to write the type of a function as param -> return_value and you see exactly that here. So the type of curriedAdd is int -> int -> int (or int -> (int -> int) if you like that better).

Java 8's lambda syntax goes along with this. To define such a function, you write

a -> b -> a + b

which is very much similar to actual lambda calculus:

λa λb a + b

λb a + b is a function that takes a single parameter b and returns a value (the sum). λa λb a + b is a function that accepts a single parameter a and returns another function of a single parameter. λa λb a + b returns λb a + b with a set to the parameter value.

Comments

0

After reading the answers, I felt the need to add examples using user interfaces. These examples can improve the understanding of currying.

interface SetLeft {
    SetRight addLeft(int left);
}

interface SetRight {
    int addRight(int right);
}
public static void main(String[] args) {

        SetLeft lambdaSum = l -> r -> l + r;
        int summa = lambdaSum
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);

        //This is the same expression, expanded into anonymous classes
        SetLeft lambdaDetail = new SetLeft() {
            @Override
            public SetRight addLeft(int l) {
                return new SetRight() {
                    @Override
                    public int addRight(int r) {
                        return l + r;
                    }
                };
            }
        };

        summa = lambdaDetail
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);
    }

And another example for using user interfaces and the Builder pattern:

@FunctionalInterface
interface AddPersonId {
    AddPersonName withId(Integer id);
}

@FunctionalInterface
interface AddPersonName {
    AddPersonDoB withName(String name);
}

@FunctionalInterface
interface AddPersonDoB {
    Person withDoB(LocalDate dob);
}

class Person {
    private Integer id;
    private String name;
    private LocalDate datOfBirth;

    public Person(Integer id, String name, LocalDate datOfBirth) {
        this.id = id;
        this.name = name;
        this.datOfBirth = datOfBirth;
    }

    static AddPersonId build() {
        
        return id 
        -> name 
        -> dob 
        -> new Person(id, name, dob);
    }
}


public class Main {
    public static void main(String[] args) {
        Person person = Person.build()
                .withId(1)
                .withName("Bob")
                .withDoB(LocalDate.parse("1985-01-01"));
    }
}

Note that in the chain of interface methods, the first one returns the next interface type, and the last one returns an object.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.