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?
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.
You need a new function (Int, Int) -> Int
, which is the sum of two (Int, Int) -> Int
functions.
Therefore, what compose(...)
returns is another (Int, Int) -> Int
typed function.
Now, we have the type of the function compose
. It returns a function, not an Int
value.
fun compose(a: (Int, Int) -> Int, b: (Int, Int) -> Int): (Int, Int) -> Int
How about its body?
It will return a function. Let's just return a lambda expression { x: Int, y: Int -> a(x, y) + b(x, y) }
.
fun compose(a: (Int, Int) -> Int, b: (Int, Int) -> Int): (Int, Int) -> Int {
return { x: Int, y: Int -> a(x, y) + b(x, y)}
}
Now we can omit all the unnecessary parts.
fun compose(a: (Int, Int) -> Int, b: (Int, Int) -> Int): (Int, Int) -> Int =
{ x, y -> a(x, y) + b(x, y) }
That's it.
Another worthwhile approach is to use infix extension function. For your case with parameters of type Int
it can be like this:
// Extension function on lambda
private infix fun ((Int, Int) -> Int).plus(f: (Int, Int) -> Int) = {
p1: Int, p2: Int, p3: Int, p4: Int -> this(p1, p2) + f(p3, p4)
}
// Usage
val first: (Int, Int) -> Int = { a, b -> a + b }
val second: (Int, Int) -> Int = { a, b -> a - b }
// first two parameters (1 and 2) for the `first` lambda,
// second two parameters (4 and 3) for the `second` lambda
val sum = (first plus second)(1, 2, 4, 3) // result is 4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With