Kotlin : When if-else is too mainstream

There used to be a Krazzy Kotlin Kid.

So, the story is just that the kid was fed up of writing verbose if (a) { } else { } and really missed the ternary operator from Java. The biggest pain was having to include braces after the if keyword. If only it was like Swift, in which we can omit brackets and they still have the ternary operator.

Source

So, she decided to play with extension functions and infix keyword. Here’s the result :

@Test
fun testIfElze() {
val condition = true
val
output = condition then { 1 + 1 } elze { 2 + 2 }
assertEquals(output, 2)
}

How? Iteration 1

Things are simple when we only wanted to execute the first block if the condition is true and execute elze block if the condition is false. We just needed to create the following two extension functions :

infix fun Boolean.then(action : () -> Unit): Boolean {
if (this)
action.invoke()
return this
}

infix fun Boolean.elze(action: () -> Unit) {
if (!this)
action.invoke()
}

Kotlin gives us the ability to omit the dot(.) and brackets while calling a function with the infix keyword.

We create an infix extension function, then() on Boolean which takes a lambda and call .invoke() on it, if the condition is true. Note, that the extension function returns a Boolean because we want to invoke the elze block on the output of the first block.

We create another infix extension function, elze() on Boolean which takes in the else block as lambda and calls .invoke().

Here we go :

val condition = true
condition then { print("Some Message") } elze { print("Some other message") }

But, this doesn’t do much. By using the above extension functions, we can not capture values from our blocks. Time to improve it!

How? Iteration 2

Now we want to capture the value that is returned by our if and elze blocks. To do this and still be able to get the syntactic sugar of calling elze on the block, we need to somehow return the value from the block and also pass on the condition Boolean. i.e. we need to return two things from the first block. To do this we can take advantage of optionals because optionals can hold two things, i.e the value itself and nullability. Let’s modify our extension functions.

infix fun <T>Boolean.then(action : () -> T): T? {
return if (this)
action.invoke()
else null
}

infix fun <T>T?.elze(action: () -> T): T {
return if (this == null)
action.invoke()
else this
}

You can find complete gist here.

Caution!

The above code might not be production ready as it could easily get out of hand if we try to access a global variable from within the blocks. We have a chance of leaking the container class. So use with care for simple operations only.