{{announcement.body}}
{{announcement.title}}

[Kotlin Pearls] Lambdas With a Context - ProAndroidDev

DZone 's Guide to

[Kotlin Pearls] Lambdas With a Context - ProAndroidDev

Boost your lambda expressions with Receivers!

· Java Zone ·
Free Resource

First, thank you very much for the incredible feedback on my previous article of the series! You can read more about it here.
This post came out a little too long, but there are so many interesting things to say about Kotlin lambdas, so let's get started!

Function Literals Are Lambdas

When Java 8 came out (March 2014), everybody was very excited about lambda expressions.

Kotlin 1.0 was released two years later (2016), and the concept of lambda expressions was already taken for granted in the language.

Lambdas work exactly as function literals.

val x: Double = 5.0                   //Double literal
val name: String = "Uberto"           //String literal
val square: (Int) -> Int = { it*it }  //Function literal
//example of use
fun main(){
    println(x)          // 5.0
    println(name)       // Uberto
    println(square(3))  // 9
}

Higher Order Function Literals

As usual in Kotlin, you can also use functions instead of simple types as parameters or result of the lambda expressions.

Functions that take other functions as input or output are called Higher Order Function, aka HOF.

val apply5: ((Int) -> Int) -> Int = { it(5) }
val applySum: (Int) -> ((Int) -> Int) = 
     { x -> {it + x} }
val applyInverse: ((Int) -> Int) -> ((Int) -> Int) = 
     { f -> { -1 * f(it) } }
//use
fun main(){
    println( apply5{ it * it }  )  //25
    println( applySum(4)(7) )   //11
    println( applyInverse{ it * it }(5) ) // -25
}


If you find the signature and the use of these lambdas confusing, you may want to try them yourself on your computer until you are quite comfortable with their use.

Being fluent at using HOF in Kotlin is a very handy skill.

Function Literals With a Receiver

Things become even more interesting if you use the extension function syntax in the lambdas. Kotlin documentation refers to it as function literals with a receiver.

As an example, compare squared with squared2 here:

fun square(x: Int): Int =  x*x
fun squared(a: Int, f: (Int) -> Int): Int = f(square(a))
fun squared2(a: Int, f: Int.() -> Int): Int = f(square(a))

//use example
fun main(){
    println(square(5))                    // 25
    println(squared(5){ it * 2}   )       // 50
    println(squared2(5){ this * 2}   )    // 50
}


Ok, so what’s the deal? The only difference is using it or this as the implicit reference!

Well, if we use classes that have methods, you can see how the code becomes more natural:

interface ExtService
data class ShopArticle(val id: Int)
data class Client(private val extService: ExtService){
    fun login() {/* some logic here using extService */}
    fun buyStuff(article: ShopArticle) {/* some logic here*/}
    fun pay() {/* some logic here using extService*/}
    fun receiveStuff(): Boolean = true //some logic here
}
class HttpService: ExtService {
    fun process(block: Client.() -> Boolean) {
        val u = Client( this) //
        if (block(u))
            println("Success!")
        else
            println("Failure!")
    }
}

//use example, almost a DSL
fun main(){
    HttpService().process {
        login()
        buyStuff(ShopArticle(42))
        pay()
        receiveStuff()
    }
}


Here, we have a Client class with some business logic that needs aHttpService to run. The HttpService creates the Client with a reference to itself. Then, let the user of the class decide the actual behavior in the block passed to the process method.

The example is intentionally stripped of all realistic details; the intention is only to show the syntax.

Now, things become even more interesting if you use your receiver to add extension methods to other types:

class ShopService(): ExtService {
    fun process(block: Order.() -> Boolean) {
        val u = Order( this) //
        if (block(u))
            println("Success!")
        else
            println("Failure!")
    }
    fun fetchArticleById(id: Int): ShopArticle = ShopArticle(id)
}
class Order(val extService: ShopService) {
    val client = Client(extService)
    fun Int.toArticle() : ShopArticle = extService.fetchArticleById(this)
    fun Client.quickOrder(article: ShopArticle) : Order {
        this.login()
        this.buyStuff(article)
        this.pay()
        return this@Order
    }
}
//use example, with new methods to Client and Int
fun main(){
    ShopService().process {
        client.quickOrder( 42.toArticle())
        client.receiveStuff()
    }
}


Here, we used the receiver (Order) to add new methods to Client and Int. It is almost like giving “superpowers” to our classes!

Finally, what if we need another implicit injection?

Let’s imagine a WebServer that can retrieve a database connection from the pool. The server will start the transaction and will commit or rollback at the end. We don’t want to expose the connection pool and transactions methods to the business logic.

In a similar fashion, the request already knows the data about the authenticated user, and we want to pass it directly to the business logic block.

interface MyConnection {
    fun getOrders(userId: Int): List<Order>
}
data class User (val id: Int, val name: String)
class MyHttpServer(val dbConn: MyConnection) {

    private fun getAuthenticated(): User = TODO()
    // Allow block to use a safely opened connection to db
    fun <T> contextConnection( block: MyConnection.() -> T) = block(dbConn)
    // Allow block to use the authenticated user 
    fun <T> contextUser( block: (User) -> T) = block(getAuthenticated())

    // Allow block to use both (this and it)
    fun <T> contextConnAndUser( block: MyConnection.(User) -> T) = block(dbConn, getAuthUser())
}


And these are three examples of use:

contextConnection{
    getOrders(123)
}
contextUser{
    println("Hello ${it.name}")
}
contextConnectionAndUser {
    println("Hello ${it.name}")
    getOrders(it.id)
}


All sources for this post can be found on GitHub.

Topics:
kotlin ,kotlin api ,java ,tutorial ,reciever ,hof ,higher order function literals ,higher order function ,lambda ,lambda expressions

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}