The ternary "if" operator is a syntactic construct for returning one of two values based upon a boolean condition. In most C-family languages its syntax resembles condition ? ifTrue : ifFalse. Some other languages, like Python, use syntax like ifTrue if condition else ifFalse or if condition ifTrue else ifFalse. What are popular options for syntax, and what are their advantages and disadvantages?
15 Answers
Here are some options (including the ones you mentioned):
C-family languages (e.g., C, C++, Java, JavaScript):
Syntax:condition ? ifTrue : ifFalse- Advantage: This syntax is widely adopted and familiar to developers working with C-family languages. It is concise and expressive.
- Disadvantage: It could be a bit less readable than other options, like Python, especially as it doesn't have the keywords
ifandelse. It also creates ambiguity with other operators like the trailing?operator.
Python:
Syntax:ifTrue if condition else ifFalse- Advantage: The Python syntax reads naturally, resembling a sentence. It can be more readable, especially when the expressions within the ifTrue and ifFalse branches are longer.
- Disadvantage: The order of ifTrue and ifFalse might be less intuitive for developers accustomed to other languages' ternary syntax. The difference in syntax may lead to errors when transitioning between languages. It's also not the same order as a normal
ofstatement. Also, the first thing to be evaluated is theconditionin the middle, which might be a bit counter-intuitive.
R/Excel:
Syntax:ifelse(condition, ifTrue, ifFalse)- Advantage: It's a lot clearer if you have multiple nested conditions, because in the alternatives you would have to insert parentheses for readability.
(Thinka ? b ? c : d : evs.ifelse(a, ifelse(b, c, d), e)) - Disadvantage: It doesn't actually have the keywords
ifandelsenext toifTrueandifFalse, so it could appear less readable.
- Advantage: It's a lot clearer if you have multiple nested conditions, because in the alternatives you would have to insert parentheses for readability.
Scala 2/Kotlin:
Syntax:if (condition) ifTrue else ifFalse- Advantage: It's in the same order as basically every language's normal
ifstatement, so it seems more natural. - Disadvantage: You have to write out parentheses around the condition, which is not required in any other syntax mentioned here.
- Advantage: It's in the same order as basically every language's normal
Swift/Rust:
Syntax:if condition { ifTrue } else { ifFalse }- Advantage: Exactly like a normal
ifstatement in most languages. This makes it more natural to write. - Disadvantage: Two pairs of brackets in a ternary operator?! Are you kidding me?
- Advantage: Exactly like a normal
F#/Scala 3:
Syntax:if condition then ifTrue else ifFalse- Advantage: Once again, in the order of a normal
ifstatement, making it more natural. - Disadvantage: Seems quite verbose, especially if they're nested.
- Advantage: Once again, in the order of a normal
Nim:
Syntax:if condition: ifTrue else: ifFalse- Advantage: Exactly like a normal
ifstatement, so it's very natural. - Disadvantage: Not too bad, but it still does need the keywords
ifandelsealong with two colons.
- Advantage: Exactly like a normal
-
1$\begingroup$ Swift: parens optional, but braces required —
if a { b } else { c }. This syntax had been used solely for statements for a while (opting for C’s?:for the expression), but was recently approved for expressions as well. $\endgroup$Bbrk24– Bbrk242023-05-16 18:27:07 +00:00Commented May 16, 2023 at 18:27 -
1$\begingroup$ Another disadvantage of
?and:is the ambiguity it creates with other common syntax, like trailing?operators $\endgroup$rydwolf– rydwolf2023-05-16 18:31:30 +00:00Commented May 16, 2023 at 18:31 -
1$\begingroup$ An advantage for the Rust syntax (not sure if it applies to Swift as well) is that the ifTrue and ifFalse sections are complete blocks, so they can have their own scope for internal required calculations instead of being a single expression. $\endgroup$kouta-kun– kouta-kun2023-05-16 20:42:02 +00:00Commented May 16, 2023 at 20:42
-
1$\begingroup$ @kouta-kun Swift distinguishes
ifstatements (where the branches are full blocks) andifexpressions (where each branch must be a single expression). $\endgroup$Bbrk24– Bbrk242023-05-17 04:11:37 +00:00Commented May 17, 2023 at 4:11 -
3$\begingroup$ @KarlKnechtel: It's called lazy evaluation. Most popular languages are eager by default, but there are some (e.g. Haskell) which are lazy by default. Some languages which are eager by default still allow lazy evaluation as an option (e.g. Scala). Even languages that don't, often have some builtin constructs which are lazy, for example, in many languages the boolean operators are lazy in their right operand. In R, specifically, function arguments are Promises. See langdev.stackexchange.com/a/275/854 $\endgroup$Jörg W Mittag– Jörg W Mittag2023-07-01 07:26:22 +00:00Commented Jul 1, 2023 at 7:26
There's always the Smalltalk postfix form.
result = condition ifTrue: [ ... ] ifFalse: [ ... ].
This has the benefit of not even being special syntax. It's just an ordinary application of the ifTrue:ifFalse: method on a Boolean object.
If your language supports lazy evaluation, if can be a regular function instead of a special form.
For example, in Mathematica, If is simply a built-in function that takes three arguments:
If[condition, t, f]
This function has the attribute HoldRest, which means that the arguments except the first one are not evaluated before the function is called.
Haskell also supports lazy evaluation, and you can define your own if function easily. I don't know why it chooses to use a special syntax for if instead of a function.
-
$\begingroup$ In Haskell, it's probably for readability:
if f x then g y else h zis more readable thanif (f x) (g y) (h z). $\endgroup$Adamátor– Adamátor2023-07-06 15:43:55 +00:00Commented Jul 6, 2023 at 15:43 -
$\begingroup$ @xigoi Personally, I find the latter more readable. $\endgroup$user76284– user762842023-07-06 15:50:48 +00:00Commented Jul 6, 2023 at 15:50
-
1$\begingroup$ TSQL and VB implement it as a function this way, named
IIFforInline If$\endgroup$James– James2023-07-11 21:48:05 +00:00Commented Jul 11, 2023 at 21:48 -
$\begingroup$ There's a page on the Haskell wiki about this: wiki.haskell.org/If-then-else. (I agree that a plain function would be better.) Feel free to link to it to your answer. $\endgroup$user76284– user762842023-07-15 02:14:42 +00:00Commented Jul 15, 2023 at 2:14
-
1
One option is SQL's CASE WHEN foo THEN bar ELSE baz END. Advantages, in addition to the very clear syntax, are that it also can be used as a binary operator CASE WHEN foo THEN bar END (defaulting to NULL) and as a superternary operator:
CASE
WHEN foo THEN 1
WHEN bar THEN 2
WHEN baz THEN 3
ELSE -1
END
The only disadvantage I can see is its verbosity, requiring 8 tokens in the ternary case instead of the 5 tokens of the less verbose languages.
-
1$\begingroup$ This does not appear to answer the asked question $\endgroup$Starship– Starship2023-07-06 09:53:53 +00:00Commented Jul 6, 2023 at 9:53
-
5$\begingroup$ @Starship-OnStrike How so? The first example does exactly the same thing as
foo ? bar : bazin C-like languages. A language designer could choose to limit the syntax to the ternary case or to support non-ternary usage as well. $\endgroup$Egyptian Natural Gas Company– Egyptian Natural Gas Company2023-07-07 13:36:53 +00:00Commented Jul 7, 2023 at 13:36
In an expression-oriented language such as Rust:
result = if condition { ifTrue } else { ifFalse }
There are three clauses, which can be permuted six ways:
if P then A else BA if P else BA else B if Pif P fails B otherwise AB unless P then AB except A if P
There are four slots in which to stick a piece of syntax (before, after, between the first and second, and between the second and third). In the examples above, I either put a keyword before each clause, or keywords between two clauses, but you can spell these however you like.
You could take a page from ML-family languages:
if p a b
But a stack-based language in RPN could be even more terse:
BAP?
Edit: This one actually does have implications for the semantics. If there is no syntax separating the two branches explicitly, efficient short-circuiting is only possible if the runtime can figure out which commands are part of each branch of the tree statically, without evaluating both. So, for example, we can’t have a Σ command that pops the number of terms to add together off the stack, then consumes that number of terms from the stack. It would then be impossible to determine which tokens not to evaluate, without evaluating them.
Instead of keywords, you might use the offsides rule:
ifThenElse p
A
B
Although it’s handy to be able to write these as a one-liner too. if you want these to be options, there should generally be some transformation between lists using indentation and those using separators.
-
$\begingroup$ If the expression requires exploiting the offside rule in a language that already ascribes meaning to that, it doesn't seem to have any real point - certainly it doesn't appear to have much of an advantage over actually using an
ifstatement. This sort of thing also doesn't embed neatly as a subexpression of more complex expressions. $\endgroup$Karl Knechtel– Karl Knechtel2023-07-11 01:43:01 +00:00Commented Jul 11, 2023 at 1:43 -
$\begingroup$ In stack-based languages (and some others), on the other hand, the distinction between statements and expressions may well be moot. Still, I like this answer for the comprehensive overview of ways to construct the syntax. $\endgroup$Karl Knechtel– Karl Knechtel2023-07-11 01:57:27 +00:00Commented Jul 11, 2023 at 1:57
-
$\begingroup$ For stack-based languages, you have FORTH, which use
IF iftrue ELSE iffalse THEN, with the condition taken from the stack before the IF. The code in either the iftrue or iffalse will be executed and can do whatever it wants with the stack (consume values or push new ones), though the results can be difficult to understand if the two cases don't have the same delta stack depth. $\endgroup$Chris Dodd– Chris Dodd2023-09-02 00:47:06 +00:00Commented Sep 2, 2023 at 0:47
One interesting option, if your language has an Optional type, is to forgo having a traditional conditional expression structure entirely, but instead have operators designed for working with Optionals to achieve the same results conveniently:
c ? ewould mean "ifcevaluates to true, then theOptionalcontaining the result of evaluatinge, otherwise the emptyOptional"o : awould mean "ifois a non-emptyOptionalthen its content, otherwise the result of evaluatinga"
These two simple operations combine together to give the same behaviour as a C-style expression of the same form, and in most circumstances a relatively simple optimizer should be able to eliminate the creation of the Optional object, giving the same performance as a direct implementation. They simplify parsing because you no longer need a special case for a ternary operator.
Note that because these operators have arguments that are expressions and they need to be able to control whether or not these expressions are evaluated, they either need to be language-level constructs (rather than simply defined using an operator overloading facility) or you need some other special faciity that allows arbitrarily short-circuiting evaluation (e.g. lazy evaluation or some kind of macro system that can operate on expressions as arguments)
Other syntaxes are, of course, also possible with similar semantics, e.g.:
Optional.ifTrue(c, e).orElse(a)
(which looks a bit like Java, but can't be implemented in Java for the reasons explained above)
-
$\begingroup$ Or you could make an
Optionalunit type your language’s Boolean, andmaptheconstfunction to it.. $\endgroup$Davislor– Davislor2024-09-18 21:51:47 +00:00Commented Sep 18, 2024 at 21:51
An option I haven't seen is to preserve the order of C-like languages, but using keywords: condition then ifTrue else ifFalse. (This could also be described as "F# syntax, but without the leading if".)
There's the Lisp style:
(IF condition then-expression [else-expression])
Lisp doesn't distinguish between statements and expressions, so this is both an if-statement and a ternary.
It's also a specialization of the more general COND:
(cond
(condition1 body1...)
(condition2 body2...)
...
[(t else-body)]
)
Ada has conditional expressions if is one, case is another, they enclosed in parenthesis:
(if Casey.Sex = M then "Male" else "Female")
from the Ada 2022 RM.
Ruby does not distinguish between statements and expressions – everything is an expression. Therefore, the traditional if/then/else can be used as an expression.
It is typically written as:
if condition
consequence1
elsif other_condition
consequence2
else
consequence3
end
However, the line break to separate the condition from the consequence can also be written as a semicolon ; or as the keyword then, so the following are also both valid (and semantically equivalent):
if condition then consequence1 elsif other_condition then consequence2 else consequence3 end
if condition; consequence1 elsif other_condition; consequence2 else consequence3 end
Ruby also has the ternary conditional operator condition ? consequence1 : consequence3, but I personally consider it superfluous. The conditional operator's precedence in combination with Ruby allowing the leave out parentheses around argument lists in message sends also sometimes surprises programmers, whereas substituting the conditional expression into the same piece of code has the expected precedence.
VB.Net also features a fairly decent approach that is similar to R's approach (which was mentioned in this answer). There are two syntaxes:
result = IIf(expression, trueresult, falseresult)result = If(expression, trueresult, falseresult)
The lattermost form was added later than the former, and obviously differs only by a letter, but has parity with the existing If syntax for ordinary if statements.
The conditional operator can be implemented without any special syntax or semantics beyond those of ordinary functions, even in strict languages.
In lambda calculus notation, let
id = λa.a
true = λa.λb.a
false = λa.λb.b
if = λp.λa.λb.p a b id
Then, for example:
if true (λx.then) (λx.else)
→ true (λx.then) (λx.else) id
→ (λx.then) id
→ then
Note that the branches are wrapped in thunks to prevent their contents from being evaluated eagerly.
Stack based langauges that directly use RPN tend to have quite different syntaxes for conditionals, built up from their underlying stack operations.
In FORTH, you have words that compile directly to (conditional) branches and branch targets:
IF: pop stack and conditional branch forward if value popped is 0 (false)ELSE: unconditional forward branch + target of last unresolved IF branchTHEN: target of last unresolved IF or ELSE branch
That gives you code that looks like
cond IF iftrue ELSE iffalse THEN
where cond is RPN code that leaves a boolean value on the stack, after which either the iftrue or iffalse code will run depending on whether that value was non-zero or zero.
In Postscript, things are built around the {...} construct that compiles stored procedures -- any code in curly braces is compiled and a reference to the compiled code is pushed on the stack. That gives you code that looks like
cond {iftrue} {iffalse} ifelse
where ifelse just pops three things from the stack (condition and two stored procedure refs) and executes one of the stored procedures. If either of the latter is not a stored procedure, it will just be pushed back on the stack, so if you just want a value in either slot (rather than actual conditional execution), the braces are unneeded.
C# switch expression is almost directly analogous to SQL’s case expression.
Both can do exactly what a ternary expression does.
bool b = f(1);
var result = b switch
{
true => "One",
false => "Two",
};
But are more powerful because they are not limited to just a single condition and so are easier to read with multiple conditions.
var result = i switch
{
1 when y==2 => "One",
1 when y>4 => "Two",
3 => "Three",
44 => "Forty Four",
};
```
-
1$\begingroup$ This isn't a syntax option for a ternary/conditional expression, it's a different language construct. $\endgroup$kaya3– kaya32023-09-10 22:37:45 +00:00Commented Sep 10, 2023 at 22:37
-
2$\begingroup$ @kaya3: a ternary expression is just a switch expression which requires exactly 2 values, instead of 1 or more. $\endgroup$jmoreno– jmoreno2023-09-11 00:05:23 +00:00Commented Sep 11, 2023 at 0:05
-
$\begingroup$ @kaya3's point still stands $\endgroup$Seggan– Seggan2023-09-11 15:54:24 +00:00Commented Sep 11, 2023 at 15:54
-
$\begingroup$ @Seggan: look at my first example again, and tell me how it is not a ternary -- if the language description for that said "syntactic construct for returning one of two values based upon a boolean". True, I am pointing out that it would be better to describe it as "syntactic construct for returning one of N values based upon the first matching condition", but it's a valid syntax for a ternary. $\endgroup$jmoreno– jmoreno2023-09-11 16:36:01 +00:00Commented Sep 11, 2023 at 16:36
-
1$\begingroup$ (1) As I said, it's a different language construct; the fact that you can use it as a substitute for a ternary expression does not make it a syntax option for a ternary expression; (2) If you are proposing this as a syntax option for a ternary expression, it's a pretty bad option. It's rather verbose since it spells out
trueandfalseas well as all the other punctuation; and I don't think any major style guide would recommend this, even in languages which lacked a proper ternary conditional operator. $\endgroup$kaya3– kaya32023-09-11 17:07:24 +00:00Commented Sep 11, 2023 at 17:07