Site icon Hari Vignesh

Creative function composition

Function composition

Function composition is a technique to compose a function using multiple functions. This will allow us to achieve the following.

If you would like to learn more about function composition or functional programming, you can refer the following series

Let’s take a look on how a function composition is done on a usual way. For the sake of simplicity, I’m going to consider a simple square and triple example.

fun square(value: Int): Int = value * value
fun triple(value: Int): Int = 3 * value

fun compose(
    square: (Int) -> Int,
    triple: (Int) -> Int
): (Int) -> Int = { value -> square(triple(value)) }

val squareOfTriple: (Int) -> Int = compose(::square, ::triple)

println(squareOfTripleOp(3)) // output -> 81
Kotlin

This above composition is all well and good. But you may want to replace the property function squareOfTriple with an actual function. One such scenario is when we need to overload the function. Such as,

object Math {

    fun calculate(value: Int): Int = square(triple(value))

    // we obviously don't need a new function just for this Long type
    // consider this example only for understading the situation
    fun calculate(value: Long): Int = square(triple(value.toInt()))

    private fun square(value: Int): Int = value * value
    private fun triple(value: Int): Int = 3 * value
}
Kotlin

For such situations, we cannot use functional references, as our function calcualte() is no longer a property function + we cannot overload the property based on return value. So, we need to use functions for sure.

if we want to use reusable composition with functional references, we can avoid the use of square(triple(value)) like the example below

Creative compositon using extensions

The function composition mentioned in the previous section is perfectly fine. But let’s get a bit more creative. To make you understand the final result, I’m going to go ahead and break the spoilers. Our final outcome should be like the following

object Math {
    
    fun calculate(value: Int): Int = 
        ::square.then(::triple)(value)

    fun calculate(value: Long): Int = 
        ::square.then(::triple)(value.toInt())

    // we can chain as much as we can ->
    // ::square.then(::triple).then(::square)

}
Kotlin

Interesting right? Let’s get to work.

To achieve this, we need to create an extension function for the function of type ‘(?) -> ?’ of name ‘then’ which composes our square of triple for us

fun <T, U, V> ((T) -> U).then(other: (U) -> V): (T) -> V = 
    { other(this(it)) }
Kotlin

with the above extension function then() we can get the following composition ::square.then(::triple)(value)


But why stop here? Let’s take our creativity a one step further. As before, let’s set our expectation

object Math {
    
    fun calculate(value: Int): Int = 
        (::square then ::triple)(value)

    fun calculate(value: Long): Int = 
        (::square then ::triple)(value.toInt())

}
Kotlin

If you have wrestled with extension functions in kotlin, you know where I’m getting at. Yes, you are right! the infix function

infix fun <T, U, V> ((T) -> U).then(other: (U) -> V): (T) -> V = 
    { other(this(it)) }
Kotlin

Creative compositon using operator overloading

Kotlin allows us to provide implementations for a predefined set of operators on our types. These operators have fixed symbolic representation (like + or *) and fixed precedence. To implement an operator, we provide a member function or an extension function with a fixed name, for the corresponding type, i.e. left-hand side type for binary operations and argument type for unary ones. Functions that overload operators need to be marked with the operator modifier. (source)

Now that we know what overloading an operator means, let’s get creative and put that in motion. Asusal, here’s our expectation

object Math {
    
    fun calculate(value: Int): Int = 
        (::square + ::triple)(value)

    fun calculate(value: Long): Int = 
        (::square + ::triple)(value.toInt())

}
Kotlin

To achieve this, we need to create an extension function for the function of type ‘(?) -> ?’ by overloading the operator plus ‘+’ which composes our square of triple for us

operator fun <T, U, V> ((T) -> U).plus(other: (U) -> V): (T) -> V = 
    { other(this(it)) }
Kotlin

By overloading the plus operator, we were able to achieve our expectation. Feel free to overload your favorite operator like rangeTo etc.

Final thoughts

Personally, as much as overloading an operator is fun for this usecase, this might confuse people a lot and it might send wrong signal for overloading the operator. I’m comfortable to use extension function. Rest, I’ll leave the decision to you!

I hope you found this piece like an art to admire! If you think you have learned something new or if you like this series or you want to boost my morale, please share the post. Thanks a lot for reading this article and thanks a lot for your time!

Exit mobile version