Add ternery operator

I love Kotlin, it’s so neat and elegant. :+1: :+1: :+1:

When I learned about Kotlin a few years ago, I was surprised to find that I couldn’t write a ? b : c, which is common in many other languages. Of course, I can use if (a) b else c instead, but it’s not neat.

After using Kotlin for a few years, I still think that the ternary operator should be supported. It’s the only obvious disadvantage I can think of (compared to Java).

I noticed this post, but it’s closed. Hope the ternery operator can be reconsidered.

1 Like

Been, discussed to death in many other threads. It ain’t gonna happen, thank goodness.

3 Likes

I will say however here is a utility method I created for our code base that comes in handy sometimes:

inline fun <T> Boolean.map(ifTrue: T, ifFalse: T) =
    if (this) ifTrue else ifFalse

which allows you to say

    a.map(b, c)

though it may not cover all cases

Yes, it’s not ideal because b and c will always be evaluated.

I do like the C-based conditional ternary operator. But, there are actually more languages that do not support it - such as some recent ones (eg. Rust, Julia, Nim and Crystal) and older popular ones like Python and Lua. Scala which inspired some aspects of Kotlin also chose not to implement it either.

  1. Python
  2. Rust
  3. Crystal
  4. Nim (though it has a similar construct)
  5. MATLAB
  6. Pascal
  7. COBOL
  8. Fortran
  9. Lua
  10. Julia
  11. Erlang
  12. Elixir
  13. Ada
  14. Modula-2
  15. R
  16. TCL
  17. Smalltalk
  18. Prolog
  19. Bash and most shell scripting languages
  20. Scratch
  21. Scala
  22. Kotlin

Lisp family:
23. Common Lisp
24. Emacs Lisp
25. AutoLisp
26. Clojure
27. ISLISP
28. PicoLisp
29. newLisp

Scheme family:
30. Racket
31. Chez Scheme
32. Chicken Scheme
33. Gambit
34. Guile
35. MIT Scheme
36. Bigloo
37. Kawa

Forth family:
38. ANS Forth
39. fig-Forth
40. iForth
41. SwiftForth
42. gForth
43. pForth
44. ColorForth
45. 8th

BASIC family:
46. Visual Basic
47. VBScript
48. B4X (B4A, B4i, B4J, B4R)
49. QBasic
50. FreeBASIC
51. PureBasic
52. Liberty BASIC
53. SmallBASIC
54. BBC BASIC
55. Gambas

ML family:
56. Standard ML
57. OCaml
58. F#

Hardware Description Languages:
59. VHDL
60. Verilog

  1. ABAP
  2. Logo
  3. Alice
  4. Inform 7
  5. APL
  6. J
  7. Occam
  8. Oberon
  9. Io
  10. Eiffel
  11. Mathematica/Wolfram Language
  12. LabVIEW
  13. REXX
  14. PL/I
  15. Algol
  16. Simula
  17. Maple
  18. Ladder Logic (used in PLCs)
1 Like

Yeah, I generally only use it for things that are not computed. Most often for choosing between 2 string resources for the UI based on a boolean such as this other function I have:

val Boolean.toYesNo: StringDesc get() = map(yes_label, no_label)

My two cents: As Kotlin uses ? to express nullability and has a ?: operator, the usual c ? a : b would not feel right. However, what makes the expression if (c) a else b slightly hard to read is that c is separated from a only via the closing parenthesis. We could improve this and make it more readable if we allowed dropping the parentheses and adding a then instead:

val z = if (c) a else b
val z = if c then a else b

It reads particularly better if expressions are long and need to be wrapped:

val z = if (someLongish < condition)
   someLongishA()
else
   someLongishB()

val z = if someLongish < condition
   then someLongishA()
   else someLongishB()

// Or:
val z =
   if someLongish < condition
   then someLongishA()
   else someLongishB()

That has several minor disadvantages; but worse, it doesn’t look like Kotlin. Every other conditional in Kotlin uses parens — when, while/dowhile, params to functions like require(), etc. Dropping them for this one case would be inconsistent and confusing.

Also: it gets very confusing if you need to nest conditions. And it doesn’t work so well for multi-way ifs:

if someLongish < condition
then someLongishA()
else if someOther < condition
    then someLongishB()
    else someLongishC()

(You could drop the indentation, but it’s still confusing having some else lines followed by conditions and others by values.)

I suppose you could keep the parens but allow then to be used — but does that give much benefit in the single-line case?

(Also, after almost 15 years, many people and organisations have significant investment in Kotlin code, so it’d have to be optional to avoid breaking existing code.)

2 Likes

Don’t break existing code. Make it optional. If you like the parentheses, you can still use them.

This is about the ternary operator. Your example is just a normal if statement. Of course, you could also use “then” there, but that’s off the point.

The ternary operator is confusing when using for nested conditions. See discussions on prettier for JavaScript/TypeScript. The least confusing formatting is IMO like this:

const r = 
   c1 ? a
   : c2 ? b
   : c3 ? c
   : d;

Which could be written as:

// current Kotlin
val r =
   if (c1) a
   else if (c2) b
   else if (c3) c
   else d

// proposed syntax
val r =
   if c1 then a
   else if c2 then b
   else if c3 then c
   else d

I find the proposed syntax slighty better to read. But by all means make it optional.

Anyway, for nested ifs, it’s better to use when probably.

val r = when {
   c1 -> a
   c2 -> b
   c3 -> c
   else -> d
}

With other words, the ternary operator isn’t the best choice for situations that need nested condition checks, and Kotlin already has a when statement. The ternary operator is very handy for cases when a single if statement is inlined in an expression, and the existing if in Kotlin is just slightly harder to read IMO, but adding then could make improve it. But it’s a matter of taste, of course.

And yes, for consistency, it should then also be allowed to drop the paretheses with while and for loops. That doesn’t look like current Kotlin, but does that make it bad? (The only bad thing I see that it would create inconsistency in coding style across the community.)

1 Like

Here’s a better example why I think then could improve the readability:

return if (a * x + b * y < c * z) (t - t0) * (q - p0)
   else (t + t0) * (q - p1)

// Or
return if a * x + b * y < c * z then (t - t0) * (q - p0)
   else (t + t0) * (q - p1)

// Or, since parentheses are optional in this proposal
return if (a * x + b * y < c * z) then (t - t0) * (q - p0)
   else (t + t0) * (q - p1)

In the existing parentheses-based syntax, the place where the condition ends and the statement starts is not easily seen. Of course, you could add {} braces, but that forces newlines with some formatters.

This example also makes it clear that dropping the parentheses is not a big deal. The thing that makes it easier to read is the proposed optional then.

2 Likes

This is a perfect example why a ternary operator would read better.. but yeaaaa.. kotlin designers are stubborn…

I guess this is just my opinion, but I think that a ternary is most valuable when the whole expression fits on one line. As soon as you start splitting it across multiple lines, imo an if expression looks better. idk if you’ve deliberately split it, or if that’s just how the forum has formatted it, but I would go all the way and fully separate it out, like so:

return if (a * x + b * y < c * z)
        (t - t0) * (q - p0)
   else
        (t + t0) * (q - p1)

1 Like

In my case I dislike when I’m not sure if a line is a real new line of code or not (without reading the entire previous line).

So I dislike:

if (aLongTest(p1, p2) && reallyLong() && (a * x + b * y < c * z))
	doSomething()

This syntax would be realy more readable for me

if aLongTest(p1, p2) && reallyLong() && (a * x + b * y < c * z)
	then doSomething()

And of course for ternary operator, it would also be much clearer.

val x = if aLongTest(p1, p2) && reallyLong() && (a * x + b * y < c * z)
	then doSomething()
	else doSomethingElse()