Accessing Kotlin’s Hidden Ternary Operator

Andrew Snyder
4 min readMay 20, 2022

Introduction

Just kidding, that was clickbait.

Kotlin doesn’t have a built-in ternary operator but it does actually have the ability to simulate one thanks to how hackable the language is, you just need to build it yourself.

In this article I will be walking through what infix functions are and how we can use them to build custom language features.

If you aren’t familiar with the types of functions in Kotlin, here’s a link that
explains them in detail.

https://kotlinlang.org/docs/functions.html

I also highly recommend looking at operator overloads, one of the lesser known but very powerful Kotlin features.

What is a Ternary Operator?

Several programming languages have what is called a ternary operator in order to quickly evaluate which value to return. Given a conditional value and results for each case, it will return one or the other based on some logic.

For example:

condition ? "Yes" : "No"

In Kotlin, the go-to solution for this is if/else syntax such as:

if (condition) "Yes" else "No"

That works well for non-nullable Boolean values but it won’t work with null like a ternary operator will which evaluates null as if it were false.

If we wanted to cover that scenario, we might write something like this:

if (condition == true) "Yes" else "No"

It is roughly equivalent to a ternary operator but it isn’t quite as pleasant to write.

Infix Functions

This is where infix functions are helpful.

Kotlin’s infix operator allows you to write functions that can be invoked by simply writing the function name between two values. A common example is when creating mappings you might use `id to value` where `to` is actually an infix function that creates a Pair.

We’ll want to do something similar in order to simulate a ternary operator.

Infix functions are written and behave like extension functions except they require a single value parameter.

To add strings together we might write something like this:

infix fun String.append(other: String) : String { 
return this + other
}

And then use it like this:

"abc" append "123" //abc123

Because infix functions work like an extension of extension functions, you could also write the following for the same result:

"abc".append("123")

The Solution

Now that we know what a ternary operator is and how infix functions work, we will build our solution.

The first thing to do is figure out the syntax we are going for. The usual syntax `?` and `:` are reserved in Kotlin and cannot be used, so instead in this example we will use “i” for if and “e” for else like this:

condition i "Yes" e "No"

With that in mind, the first task is to make a function to call on the condition, providing the result for when it is true. In order to do that we will need to create a data class to hold the context as we chain our functions together since each infix operator can only have a single value.

data class TernaryResult(
val trueResult: String,
val isTrue: Boolean
)

Now we can create an infix function that will return this `TernaryResult` class which the second infix function will use to complete the logic.

infix fun Boolean?.i(trueResult: String) : TernaryResult {
return TernaryResult(trueResult, this == true)
}

Then we need to write our else case function that will return either the true or false result based on the condition.

infix fun TernaryResult.e(falseResult: String): String {
return if (isTrue) {
trueResult
} else {
falseResult
}
}

With the infix functions set up, we can now call them off of each other in sequence as previously defined.

var condition: Boolean? = null
condition i "Yes" e "No" //No
condition = false
condition i "Yes" e "No" //No
condition = true
condition i "Yes" e "No" //Yes

So there you have it, a ternary operator implemented in Kotlin.

This was a simple example assuming the original value was a Boolean and that we wanted to return only a String, however this could be modified in many other ways to meet various needs. It is useful to understand all of Kotlin’s features even if you don’t fully take advantage of them all.

Thank you for reading and I hope you take away something useful from this.

-Andrew

BONUS

Because we have full control of the data class flowing between these functions, we could take it step further and add a separate condition for when the value is null.

For that, instead of using “i” and “e” , we’ll switch to using “t” for true, “f” for false and “n” for null.

There are a few changes that need to be made throughout the entire solution so far, see if you can work them out yourself otherwise the code is below.

Full Code:

data class TernaryResult(
val trueResult: String,
val falseResult: String = "Not Set",
val isTrueCondition: Boolean,
val isNullCondition: Boolean
)
infix fun Boolean?.t(trueResult: String): TernaryResult {
return TernaryResult(
trueResult,
isTrueCondition = this == true,
isNullCondition = this == null
)
}
infix fun TernaryResult.f(falseResult: String): TernaryResult {
return copy(falseResult = falseResult)
}
infix fun TernaryResult.n(nullResult: String): String {
return when {
isTrueCondition -> trueResult
isNullCondition -> nullResult
else -> falseResult
}
}
//Here’s the outcome compared to other ways of doing the same thingcondition t "Yes" f "No" n "N/A"when (condition) {
true -> "Yes"
false -> "No"
else -> "N/A"
}
if (condition == true) "Yes"
else if (condition == false) "No"
else "N/A"

--

--