Search code examples
kotlindictionaryjvmelvis-operator

Kotlin elvis operator don't work with map.get()


meet some strange behaviour of Kotlin elvis operator, when try get some value by key from map, and no one of the blocks is not execute.

map["Key"]?.let {
    println("Run block, inserting into existing list")
    it.add("value")
} ?: {
    println("Elvis block, creating list with value")
    map["Key"] = mutableListOf("value")
}

I expect, that if Key is exist, it going execute let block, print line and insert value into existing list. If key is not exist, it going to elvis block, print line, and insert new list with value. But it's not. As not let block as being execute, not elvis.

To solve my task i using:

map.getOrPut("Key") { mutableListOf() }.add("Value")

Question is, why i can't just use elvis with let operator in that situation?

Kotlin version 1.8.22


Solution

  • The expression you put on the right hand side of ?: is:

    {
        println("Elvis block, creating list with value")
        map["Key"] = mutableListOf("value")
    }
    

    This is just a lambda of type () -> Unit. Evaluating this expression just gives you a value of type () -> Unit. The code in the block is not executed.

    You should use run if you want to execute the code in the lambda:

    map["Key"]?.let {
        println("Run block, inserting into existing list")
        it.add("value")
    } ?: run {
        println("Elvis block, creating list with value")
        map["Key"] = mutableListOf("value")
    }
    

    That said, something simple like this is more readable, if you want to print different messages depending on whether the key exists:

    val list = map["Key"]
    if (list != null) {
        println("Run block, inserting into existing list")
        list.add("value")
    } else {
        println("Elvis block, creating list with value")
        map["Key"] = mutableListOf("value")
    }
    

    Of course, if you don't need to print different messages, the idiomatic way is getOrPut.