5

If I create a Functional interface:

@FunctionalInterface
public class Consumer2<T1, T2> {
    void accept(T1 t1, T2 t2);

    default Consumer1<T2> curry(T1 t1) {
        return (t2) -> accept(t1, t2);
    }
}

Now, if I have a class:

public class MyClass {
    public void printStrings(String a, String b) {
        System.out.println(a + ": " + b);
    }
}

MyClass myClass = new MyClass();

Now, if I want to use my functional interface, I can:

Consumer2<String, String> printString = myClass::printStrings;
printString.curry("hello").accept("world");

But I can't do something like:

myClass::printStrings.curry("hello").accept("world");

which makes sense, because Java has no way of knowing that myClass::printStrings can be applied to the functional interface Consumer2. To do this, I have created a utility class:

public class F {
    public static <T1, T2> Consumer2<T1, T2> c2(Consumer2<T1, T2> fn) {
        return fn;
    }
}

Then I can:

F.c2(myClass::printStrings).curry("hello").accept("world");

Even, this will work:

((Consumer2<String, String>)myClass::printStrings).curry("hello").accept("world");

As long as there is some way for Java 8 to understand that functional type in this case. So, the question is, what is the best way to do it, while possibly avoiding the boilerplate?

1
  • 1
    I guess class Consumer2 is supposed to be interface Consumer2 Commented Sep 16, 2015 at 9:44

2 Answers 2

4

You are not currying but performing partial function application. These operations are related, but not identical. Currying means to transform your Consumer2<T1, T2> to a Function<T1,Consumer1<T2>>. When applying that curried function to a T1 value you get what your method is effectively doing.

It’s easier to use the established name bind as binding a value to a function’s parameter is something, every developer understands without needing to dive deeply into the world of functional programming.

That said, it’s best to remember that now interfaces can have static methods, so there is no need for such utility classes. Further, a static method just returning its argument is of little use on its own, so you may fuse it with the follow-up method that it is supposed to serve. Then, it’s fulfilling the same purpose as the instance method and can be offered as a simple overload:

@FunctionalInterface
public interface Consumer2<T1, T2> {
    void accept(T1 t1, T2 t2);

    default Consumer1<T2> bind(T1 t1) {
        return bind(this, t1);
    }
    static <T,U> Consumer1<U> bind(Consumer2<? super T, ? super U> c, T t) {
        return u -> c.accept(t, u);
    }
}
public interface Consumer1<T1> extends Consumer<T1> {}

public class MyClass {
    public static void printStrings(String a, String b) {
        System.out.println(a + ": " + b);
    }

    public static void main(String[] args) {
        Consumer2.bind(MyClass::printStrings, "hello").accept("world");
    }
}

On the other hand, when you use the existing standard interfaces Consumer and BiConsumer you have no choice but to offer a utility method in a class different to these interfaces. But the good news is that it’s easy to make the solution consistent then, as you can’t provide anything else but a static method:

class FunctionUtil {
    static <T,U> Consumer<U> bind(BiConsumer<? super T, ? super U> c, T t) {
        return u -> c.accept(t, u);
    }
}
public class MyClass {
    public static void printStrings(String a, String b) {
        System.out.println(a + ": " + b);
    }

    public static void main(String[] args) {
        FunctionUtil.bind(MyClass::printStrings, "hello").accept("world");
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Your solution involving F.c2 method is interesting, but your example is too artificial. If you ask, how to write better this code

F.c2(myClass::printStrings).curry("hello").accept("world");

Then I would definitely advise you to write like this:

myClass.printStrings("hello", "world");

If you want to ask how to bind the predefined parameters to the method reference, I would advise you to use lambda function instead:

Consumer1<String> fn = str -> myClass.printStrings("hello", str);
fn.accept("world");

Probably you want to consider the case when your function is not known at the compile time. In this case it's either returned from another method or passed to current method as method parameter or stored in the variable/field. In all of these cases it's already the functional interface and you can use the curry directly:

Consumer2<String, String> getConsumer2() { return myClass::printStrings; }

getConsumer2().curry("hello").accept("world");

Thus in general I don't see the problem here. If you still think that applying currying to the method reference is useful, I would create a static curry method (though I guess, it's actually a "bind", not "curry") in the Consumer1 interface:

static <T1, T2> Consumer1<T2> curry(Consumer2<T1, T2> c2, T1 t1) {
    return c2.curry(t1);
}

And use it like this:

Consumer1.curry(myClass::printStrings, "hello").accept("world");

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.