Kotlin - functions are objects

Sometimes it is useful to look at a subject from a slightly different perspective, this provides a sort of mental “binocular vision” and allows to flesh out some details that are less obvious otherwise.

Pica: A function is an implementation of the Function interface and more specifically, on the JVM, an implementation of Function[n] interface where n is in {0..22} ( source on Github ). For example, Function1 is defined as:

/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}

Elster: This is not how functions are usually presented, show me what you mean.

Pica: With a bit of reflection we have:

fun x2(x: Int) = x * 2
println(::x2 is Function1<Int, Int>)   // prints true

it is also possible to spell out the type a function reference explicitly as Function[n]:

val x2ref: Function1<Int, Int> = ::x2 

Elster: Ok, I guess you can write a class that implements Function1 and multiplies it’s parameter by two?

Pica: Yes, something like this:

class X2 : Function1<Int, Int> {
   override fun invoke(x: Int) = 2 * x
}

X2() is Function1<Int, Int>   // true
X2() is (Int) -> Int          // true

and slightly cleaner version using a singleton object:

object ox2 : Function1<Int, Int> {
    override fun invoke(x: Int) = 2 * x
}

ox2 is Function1<Int, Int>   // true
ox2 is (Int) -> Int          // true

Elster: And this works? I mean, is it semantically the same as fun x2(x: Int) = x * 2 ?

Pica: It does, and it is.

val x2 = X2()

x2(3)                          // 6
listOf(1,2,3).map(x2)          // [2,4,6]
listOf(1,2,3).map(ox2)         // [2,4,6]

Elster: Wait, if in Function[n] - n has to be less than 23, what happens if I define a function with 23 parameters?

Pica: Let’s see

fun f23(p1:Int,.., p23:Int) = ""

a call to ::f23 results in a runtime exception:

java.lang.NoClassDefFoundError: kotlin/Function23      

This is in Kotlin v. 1.2.60 - there is a development goal to Get rid of 23 hardwired physical function classes.

Elster: hmm… ok, this nice and all but what is the point of this exercise?

Pica: It allows someone who is coming from OO background to match functional idioms in Kotlin to something more familiar. First of all the notation (T1,T2,..,Tn) -> R maps directly to the functional (SAM) interface Function[n]<T1,T2,..,Tn,R>. Some more examples:

Currying and partial application:

    // Kotlin idiomatic
    fun add(a: Int): (Int) -> Int = { b -> a + b }

    val add1 = add(1)
    println ( add1(2))    // 3


    // Using class based implementation
    object Add {
        fun add(a: Int): Function1<Int, Int> {
            return object: Function1<Int, Int> {
                override fun invoke(b: Int): Int = a + b
            }
        }
    }

    val add1 = Add.add(1)
    add1(2)        // 3

Accumulator function:

    // Kotlin idiomatic
    fun a(n: Int): (d: Int) -> Int {
        var accumulator = n
        return { x -> accumulator += x; accumulator }
    }

    val a100 = a(100)
    
    a100(5)    // 105
    a100(10)   // 115
    a(1)(5)    // 6

    // Using class based implementation
    object A {
        var accumulator :Int = 0

        fun a(n : Int) : (Int) -> Int {
            accumulator = n

            return object: (Int) -> Int {
                override fun invoke(n: Int): Int {
                    accumulator += n
                    return accumulator;
                }
            }
        }
    }

    val A100 = A.a(100)

    A100(5)   // 105
    A100(10)  // 115
    A.a(1)(5) // 6
kotlin