kotlinhigher-order-functionskotlin-higher-order-functions

Kotlin Higher Order Function Composition


I'm trying to figure out how I can declaritively define a function as a composition of two other functions in Kotlin but I'm struggling. Here is my code:

fun compose(a: (Int, Int) -> Int, b: (Int, Int) -> Int): Int {
    return a.invoke() + b.invoke()
}

The idea of the compose function is that it will take in two functions as it's input (both of which takes two Ints and return an Int) and return the sum of the results of the two passed functions. The problem is I have to invoke the passed functions to work out their sum (obviously lol) but I don't know the values which I wish to invoke with inside the compose method (they are the values passed to the functions).

Am I completely missing something here? I know this is possible in a language like Haskell, is it possible or not in Kotlin?


Solution

  • One way:

    You have to pass the two Ints as additional arguments to compose like this:

    fun compose(
        c: Int, d: Int, a: (Int, Int) -> Int, b: (Int, Int) -> Int
    ) = a(c, d) + b(c, d)
    

    Lambdas are a further level of abstraction which give you the opportunity to make behaviour variable, you still have to provide them with data.

    A more abstract approach:

    You can abstract even further and let compose return a lambda which combines the results of the other two lambdas (lets call it compose2):

    // return type is inferred to (Int, Int) -> Int
    fun compose2(a: (Int, Int) -> Int, b: (Int, Int) -> Int) = { 
         c: Int, d: Int -> a(c, d) + b(c, d)
    }
    
    val f = compose2(/* pass lambdas */)
    

    f is a lambda itself an can be called like this:

    f(2, 4)
    

    So, compose2 does nothing more than return a lambda which adds the results of the two passed lambdas. The actual invocation is done outside of compose2.

    An even more abstract approach:

    In your compose function you use a simple addition as composition operation. You can even make this operation variable, by passing a third lambda ab which tells your function how to compose a and b:

    fun compose3(
        a: (Int, Int) -> Int, b: (Int, Int) -> Int, ab: (Int, Int) -> Int
    ) = { 
        c: Int, d: Int -> ab(a(c, d), b(c, d))
    }
    

    The result is, again, a lambda which takes two Ints and returns one Int.