17
$\begingroup$

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?

$\endgroup$

15 Answers 15

20
$\begingroup$

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 if and else. 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 of statement. Also, the first thing to be evaluated is the condition in 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.
      (Think a ? b ? c : d : e vs. ifelse(a, ifelse(b, c, d), e))
    • Disadvantage: It doesn't actually have the keywords if and else next to ifTrue and ifFalse, so it could appear less readable.
  • Scala 2/Kotlin:
    Syntax: if (condition) ifTrue else ifFalse

    • Advantage: It's in the same order as basically every language's normal if statement, 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.
  • Swift/Rust:
    Syntax: if condition { ifTrue } else { ifFalse }

    • Advantage: Exactly like a normal if statement in most languages. This makes it more natural to write.
    • Disadvantage: Two pairs of brackets in a ternary operator?! Are you kidding me?
  • F#/Scala 3:
    Syntax: if condition then ifTrue else ifFalse

    • Advantage: Once again, in the order of a normal if statement, making it more natural.
    • Disadvantage: Seems quite verbose, especially if they're nested.
  • Nim:
    Syntax: if condition: ifTrue else: ifFalse

    • Advantage: Exactly like a normal if statement, so it's very natural.
    • Disadvantage: Not too bad, but it still does need the keywords if and else along with two colons.
$\endgroup$
17
  • 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
    Commented 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$ Commented 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
    Commented May 16, 2023 at 20:42
  • 1
    $\begingroup$ @kouta-kun Swift distinguishes if statements (where the branches are full blocks) and if expressions (where each branch must be a single expression). $\endgroup$
    – Bbrk24
    Commented 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$ Commented Jul 1, 2023 at 7:26
13
$\begingroup$

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.

$\endgroup$
8
$\begingroup$

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.

$\endgroup$
5
  • $\begingroup$ In Haskell, it's probably for readability: if f x then g y else h z is more readable than if (f x) (g y) (h z). $\endgroup$
    – xigoi
    Commented Jul 6, 2023 at 15:43
  • $\begingroup$ @xigoi Personally, I find the latter more readable. $\endgroup$
    – user76284
    Commented Jul 6, 2023 at 15:50
  • 1
    $\begingroup$ TSQL and VB implement it as a function this way, named IIF for Inline If $\endgroup$
    – James
    Commented 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
    Commented Jul 15, 2023 at 2:14
  • 1
    $\begingroup$ This can also be done in strict languages (see my answer). $\endgroup$
    – user76284
    Commented Jul 15, 2023 at 3:00
6
$\begingroup$

In an expression-oriented language such as Rust:

result = if condition { ifTrue } else { ifFalse }
$\endgroup$
6
$\begingroup$

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.

$\endgroup$
2
  • 1
    $\begingroup$ This does not appear to answer the asked question $\endgroup$ Commented Jul 6, 2023 at 9:53
  • 4
    $\begingroup$ @Starship-OnStrike How so? The first example does exactly the same thing as foo ? bar : baz in 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$ Commented Jul 7, 2023 at 13:36
4
$\begingroup$

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.

$\endgroup$
4
$\begingroup$

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.

$\endgroup$
4
$\begingroup$

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".)

$\endgroup$
4
$\begingroup$

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)]
)
$\endgroup$
4
$\begingroup$

There are three clauses, which can be permuted six ways:

  • if P then A else B
  • A if P else B
  • A else B if P
  • if P fails B otherwise A
  • B unless P then A
  • B 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.

$\endgroup$
3
  • $\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 if statement. This sort of thing also doesn't embed neatly as a subexpression of more complex expressions. $\endgroup$ Commented 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$ Commented 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
    Commented Sep 2, 2023 at 0:47
4
$\begingroup$

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 ? e would mean "if c evaluates to true, then the Optional containing the result of evaluating e, otherwise the empty Optional"
  • o : a would mean "if o is a non-empty Optional then its content, otherwise the result of evaluating a"

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)

$\endgroup$
3
$\begingroup$

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.

$\endgroup$
2
$\begingroup$

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.

$\endgroup$
2
$\begingroup$

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 branch
  • THEN: 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.

$\endgroup$
0
$\begingroup$

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",  
}; 

```
$\endgroup$
7
  • 1
    $\begingroup$ This isn't a syntax option for a ternary/conditional expression, it's a different language construct. $\endgroup$
    – kaya3
    Commented Sep 10, 2023 at 22:37
  • 1
    $\begingroup$ @kaya3: a ternary expression is just a switch expression which requires exactly 2 values, instead of 1 or more. $\endgroup$
    – jmoreno
    Commented Sep 11, 2023 at 0:05
  • $\begingroup$ @kaya3's point still stands $\endgroup$
    – Seggan
    Commented 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
    Commented 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 true and false as 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
    Commented Sep 11, 2023 at 17:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .