Lambda Sans Return cover image

Lambda Sans Return

David Kanenwisher • February 17, 2020

kotlin

I created a lambda in Kotlin the other day and was perplexed that I couldn't avoid giving it a return type. Then I heard about Unit. Supposedly, I could return Unit and not have to worry about the return type on my lambdas. I don't know much about Unit but I had to see this in action.

I took some code I've been fiddling with, a sort of observer object that I call a Dispatcher. It accepts a lambda as way to receive an event from the Dispatcher. The test for it looks like so:

package com.dkanen

import kotlin.test.Test
import kotlin.test.*

class DispatcherTest {

    @Test
    fun `when a lambda is passed to subscribe it can receive a broadcast`() {
        val dispatcher = Dispatcher()

        var passedValue = ""
        val subscribeFunction: (String) -> String = { event ->
            passedValue = event
            passedValue
        }

        dispatcher.subscribe(subscribeFunction)
        dispatcher.broadcast("event")

        assertEquals("event", passedValue)
    }
}

Did you see the subscribeFunction that has a weired second line passedValue? Kotlin's lambda like to keep things short and omit the return statement for the last line. I need to have that strange line there because I need to satisfy the type signature and return a String.

The implementation of Dispatcher that makes the test pass:

package com.dkanen

class Dispatcher {

    var subscriber: (String) -> String = { "test"  }

    fun subscribe(subscriberFunction: (String) -> String) {
        subscriber = subscriberFunction
    }

    fun broadcast(event: String) {
        subscriber(event)
    }
}

Notice how the var subscriber has a type signature of (String) -> String. This means that the lambda has to take a String as an argument and return a String. I can make it return a String for the sake of making it work. I don't like it though because it suggests to the reader they should do something with that String. Now that I know about Unit I have what I need to rid my code of this confusion.

I go in and update Dispatcher:

package com.dkanen

class Dispatcher {

    var subscriber: (String) -> Unit = {}

    fun subscribe(subscriberFunction: (String) -> Unit) {
        subscriber = subscriberFunction
    }

    fun broadcast(event: String) {
        subscriber(event)
    }
}

Notice how subscriber type signature is now (String) -> Unit.

Then I update the test:

package com.dkanen

import kotlin.test.Test
import kotlin.test.*

class DispatcherTest {

    @Test
    fun `when a lambda is passed to subscribe it can receive a broadcast`() {
        val dispatcher = Dispatcher()

        var passedValue = ""
        val subscribeFunction: (String) -> Unit = { event ->
            passedValue = event
        }

        dispatcher.subscribe(subscribeFunction)
        dispatcher.broadcast("event")

        assertEquals("event", passedValue)
    }
}

Checkout how subscribeFunction matches the type signature of of Dispatcher's subscribe by removing the implied return. Awesome sauce.

I am a bit curious about Unit but that's a topic for a different day.

What cool ways are you finding to shorten your lambdas in Kotlin?