Higher-order functions, Java8 vs. Kotlin

I decided to try something fun to see how it would work in Kotlin vs. how it works in Java8. First, here’s a simple Java8 class that uses function composition (with apologies in advance to Bruno Mars and Mark Ronson):

import org.jetbrains.annotations.NotNull;

import java.util.function.Function;

public class JavaFunc {
    @NotNull
    public static <T> Function<T,T> uptown(@NotNull Function<T,T> funkYouUp) {
        return funkYouUp.andThen(funkYouUp).andThen(funkYouUp);
    }
}

And here’s a Kotlin class that uses it, as well as tries to do the same thing itself without calling out to Java:

fun getFuncy(s: String): String = "%s %s".format(s,s)

// this, as well as something akin to Java's Function.compose, should probably be built into Standard.kt
infix fun <F: (T1)->T2,T1,T2,T3> F.andThen(g: (T2)->T3): (T1)->T3 = { g(this(it)) }

// the Java code is doing the equivalent of this line here
fun <T> uptown(funkYouUp: (T)->T) = funkYouUp andThen funkYouUp andThen funkYouUp

fun main(args: Array<String>) {
  val refrain = "Uptown func you up!"
  val func = ::getFuncy
  // val funcYouUp: (String) -> String = JavaFunc.uptown(func) // fails because Kotlin can't translate from java.util.Function
  val funcYouUp = JavaFunc.uptown(func)
  System.out.println(funcYouUp.apply(refrain)) // ugly, but works

  val funcYouUp2 = uptown(func)
  System.out.println(funcYouUp2(refrain)); // this is what I want the Java8 version, above, to look like
}

A couple things are worth noting about this exercise:

  • Standard.kt should probably have function composition operators, like andThen above, as a standard built-in feature for everybody. They’re really useful.
  • Kotlin’s interop with Java8 lambdas isn’t as good as it could be. Notably, the inferred type of funcYouUp is Function<String!,String!>! with no easy way to annotate the Java, beyond what I’ve already done with @NotNull to nuke all those ! things, much less get the inference all the way to (String)->String, which is what I really want.
  • If I try deleting the return type of andThen (i.e., remove the : (T1)->T3 part), then Kotlin cannot infer it, giving the unhelpful error “unresolved reference: it”. If I change the lambda to read { x -> g(this(x)) }, it then highlights the x and says “cannot infer a type for this parameter”. The error message for the “it” lambda form should be the same as for the declared parameter-name form, and really the compiler should be able to figure out either one without needing the return-type declaration.
  • Nonetheless, it’s exceptionally cool to be able to declare an extension function on lambdas, and have it just magically work.
1 Like