Java 8 functional interfaces and varags functions

Fallowing the article about simple Partial Function application in Java, I had made a simple experiment with handling the varags methods using lambdas and functional interfaces. For functions that we can represent in a form of SAM interface:

@FunctionalInterface
public interface VarArgFunction<R, T> {

    R apply(T... args);
}

As mentioned in the previous post, you can assign in fact the variable argument method to any number argument interfaces as long as the arguments are all of the same type:

Function f = Functions::funcVarArgs;
Function1<String, String> f1 = Functions::funcVarArgs;
Function2<String, String, String> f2 = Functions::funcVarArgs;

But also we can have our function “assigned” to the vararg interface:

VarArgFunction<String, String> varargs = Functions::funcVarArgs;

The Java 8 default methods turn out to be a very powerful together with lambda functions, because we can now define instance methods for that interface, let’s try to implement similar partial function application as previously for standard functions.

    @SuppressWarnings("unchecked")
    default VArgFunction<R, T> arg(T arg) {

        return (T... args) -> {
            final T[] arguments = (T[]) new Object[args.length + 1];
            arguments[0] = arg;
            System.arraycopy(args, 0, arguments, 1, args.length);
            return apply(arguments);
        };
    } 

Unfortunately this is the best we can get with expanding the original arguments array. We will need to create a new array and copy all of the original values into it. The benefit of that is that we can now add the function arguments at runtime for instance in loop, and we are not bound by any constraints, except of the heap memory size:


for(String arg : args) {
  varargs = varargs.arg(arg);
}

String result = varargs.apply()

Fallowed by immediate method invocation to get the computation result.

Unfortunately this implementation has one significant problem, since it will create for every arg() call new object array instance the running time of apply method will be quadratic – O(N^2). The arg itself is performing it’s work in O(1) time, but when the function gets evaluated it will be cascading the apply calls with creating for each individual call new array.

Now can we do better then that?

Actually a simple wrapper around the interface will do the job, since there is already Partial class in the library let’s expand it with one extra definition:

static <R, T> VArgFunction<R, T> vargFunction(VArgFunction<R, T> function) {

        return new VArgFunction<R, T>() {

            private final List<T> args = new LinkedList<>();

            public R apply(T... args) {
                this.args.addAll(Arrays.asList(args));
                final T[] argsArray = (T[]) Array.newInstance(args.getClass().getComponentType(), args.length);
                return function.apply(this.args.toArray(argsArray));
            }

            public VArgFunction<R, T> arg(T arg) {
                args.add(arg);
                return this;
            }
        };
    }

This time we are wrapping around the original functional interface and store the arguments in collection. We have effectively reduced the running time of apply method to linear time O(N) – proportional to number of arguments applied to the function.

This way we still provide the base functionality through default method implementation, though we can provide more specialized implementation through wrapper interface around the original lambda expression.

If you like to you can grab the code and see if you can find some interesting applications for running the variable argument function through functional interfaces.

https://github.com/jmnarloch/funava

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s