Monday 23 November 2015

Currying in Kotlin

As an example consider summing over a range of integers applying a provided function on each. Something like
fun sigma(range: IntRange, f: (Int) -> Int): Int {
    var result = 0
    for (i in range) result += f(i)
    return result
}
and we have
println(sigma(1..5) { x -> x  })    // prints: 15
println(sigma(1..5) { x -> x * x }) // prints: 55
We would like to to fix f() so that we could define:
val sigmaOfSum = sigma { x -> x }
val sigmaOfProduct = sigma { x -> x * x }
without committing to a range, such that later on, we can invoke
println(sigmaOfSum(1..5))     // prints: 15
println(sigmaOfProduct(1..5)) // prints: 55
This can be done in straight Kotlin:
fun sigma(f: (Int) -> Int): (IntRange) -> Int {
    fun applyF(range: IntRange): Int {
        var result = 0
        for (i in range) result += f(i)
        return result
    }
    return ::applyF
}
Here, the (higher order) function sigma takes f() as a parameter and returns a function that applies f() on the provided range.
A more down to earth example; suppose we want to perform a DB lookup. In principal, we need specify a key/query and the th DB instance but we want to break these two things apart in the spirit of the first example:
fun lookUp(db: Map): (String) -> String? {
    fun dbLookup(key: String): String? {
        return db.get(key)
    }
    return ::dbLookup
}
now the following works:
val customerLookup = lookUp(mapOf("1" to "Customer1", "2" to "Customer2"))
val productLookup = lookUp(mapOf("1" to "Product1", "2" to "Product2"))

println(customerLookup("1")) // prints Customer1
println(productLookup("1"))  // prints Product1
Our lookUp() function is quite simple and can be collapsed to:
fun lookUp(map: Map): (String) -> String? = { map.get(it) }   

No comments:

Post a Comment