Trailing lambda for get operator

I wrote some code like this:

class Cache<K, V>(val capacity: Int = 10) {
    val cache = LinkedHashMap<K,V>()

    fun invalidate(key: K) = cache.remove(key)

    operator fun get(key: K, onCacheMiss: () -> V): V =
        cache[key] ?: run {
            if (cache.size > capacity) {
                cache.remove(cache.keys.first())
            }
            onCacheMiss().also { cache[key] = it }
        }

}

Now, my simple brain expected that the “trailing lambda” rules would also apply to the get operator, but nope, I have to write:
cache["a", { someCalculation() }]

instead of the much nicer and more natural
cache["a"]{ someCalculation() }.

The thing is, if I change from the get operator to invoke, it works (and I’m pretty sure I have seen examples of this in the Kotlin API itself)

I know this is an edge case, but it feels like an inconsistency. What do you think?

1 Like

I found a workaround, but it’s a little mind bender:

class Cache<K, V>(val capacity: Int = 10) {
    val cache = LinkedHashMap<K, V>()

    fun invalidate(key: K) = cache.remove(key)

    operator fun get(key: K): (() -> V) -> V = { onCacheMiss: () -> V ->
        cache[key] ?: run {
            if (cache.size > capacity) {
                cache.remove(cache.keys.first())
            }
            onCacheMiss().also { cache[key] = it }
        }
    }

}

Now cache["a"]{ someCalculation() } works, cache["a", { someCalculation() }] doesn’t work anymore, and many people would be slightly confused how to use this mess.

1 Like