if/else
Table of contents
Problem
if
/else
is noted in the language overview, but is provisional. Control flow is important, and if
/else
is basic; the form is similar in many languages, even if details may change.
Background
if
/else
is a common conditional, seen in many languages. A few syntaxes that are likely to influence if
/else
are:
-
C++
if (x) { printf("x is true"); } else if (y) { printf("y is true"); } else { printf("Neither was true"); }
-
Python
if x: print("x is true"); elif y: print("y is true"); else: print("Neither was true");
-
Swift
if x { print("x is true") } else if y { print("y is true") } else { print("Neither was true") }
-
Rust – versus other cases where
if
is a statement, Rust makesif
an expression, allowing:let x = if y { 1 } else { 0 };
Proposal
We should make if
/else
syntax consistent with C and C++, rather than adopting the syntax of another language.
Details
if
/else
is a statement. The syntax looks like:
if
(
boolean expression)
{
statements evaluated when true }
[ else
{
statements evaluated when false }
]
The braces are optional, but must be paired ({ ... }
) if present. When there are no braces, only one statement is allowed.
Executable semantics form
statement:
"if" '(' expression ')' statement optional_else
| /* preexisting statements elided */
;
optional_else:
/* empty */
| "else" statement
;
Caveats
C++ as baseline
This baseline syntax is based on C++, following the migration sub-goal Familiarity for experienced C++ developers with a gentle learning curve. To the extent that this proposal anchors on a particular approach, it aims to anchor on C++’s existing syntax, consistent with that sub-goal.
Alternatives will generally reflect breaking consistency with C++ syntax. While most proposals may consider alternatives more, this proposal suggests a threshold of only accepting alternatives that skew from C++ syntax if they are clearly better; the priority in this proposal is to avoid debate and produce a trivial proposal. Where an alternative would trigger debate, it should be examined by an advocate in a separate proposal.
if/else in expressions
This proposal covers if
/else
as a statement. A Rust-like form of if
/else
as an expression could be supported, but is not part of this proposal because it’s more complex.
Indentation
It may be desirable to require meaningful indentation of the body of an if
/else
, in particular to help catch errors when there are no braces.
For example, this could be a compiler error due to inconsistent indentation of the do_parse
assignment:
if (missing_data)
Print("Missing data!");
do_parse = false;
if (do_parse)
ParseData();
This is not part of this proposal.
Ambiguous else
It may be desirable to reject cases where an else
is ambiguous. For example, this could be a compiler error due to the ambiguous else
:
if (a) if (b) f(); else g();
This is not part of this proposal. This proposal takes C++ syntax as a baseline, so an else
binds to the innermost enclosing if
that doesn’t already have an else
.
This desire might also be addressed by choosing to require consistent indentation and disallowing multiple if
s on the same line.
Alternatives considered
See C++ as baseline for an explanation of how alternatives are evaluated in this proposal.
No parentheses
Parentheses could be optional (essentially not part of if
/else
, but addable as part of the expression), instead of required (as proposed).
Advantages:
- Removing parentheses gives developers less to type.
- Consistent with several other languages, including Swift and Rust.
Disadvantages:
- Requiring parentheses is consistent with C++, and will be intuitive for C++ developers, from both a writability and readability perspective.
- Parentheses help avoid ambiguities.
- The Swift and Rust model is ambiguous if it’s ever possible for an expression to optionally be followed by braces. That is possible in Rust, at least, where
Type{...}
is a valid expression. As a result, Rust rejectsif Type{.value = true}.value { thing1 } else { thing2 }
because it misinterprets the braces.
- The Swift and Rust model is ambiguous if it’s ever possible for an expression to optionally be followed by braces. That is possible in Rust, at least, where
- Parentheses allow making the braces optional without any risk of ambiguity.
- Parentheses allow introducing syntactic variants in the future without ambiguity.
- For example, C++’s
if constexpr (...)
wouldn’t have been possible if the parentheses were optional.
- For example, C++’s
The benefits of this are debatable, and it should be examined by an advocate in a focused proposal. For now, we should match C++’s decision.
Require braces
Braces could be required, instead of optional (as proposed).
Advantages:
- Braces avoid syntax ambiguities.
- For example,
if (x) if (y) { ... } else { ... }
has difficult-to-understand binding ofelse
.
- For example,
-
Braces avoid errors when adding statements.
-
For example, if:
if (x) do_parse = false;
has a statement added:
if (missing_data) Print("Missing data!"); do_parse = false;
-
Disadvantages:
- Inconsistent with C++.
The benefits of this are debatable, and it should be examined by an advocate in a focused proposal. For now, we should match C++’s decision.
Rationale
This proposal focuses on an uncontroversial piece that we are going to carry from C++, as a baseline for future Carbon evolution. It serves our migration goals (especially “Familiarity for experienced C++ developers with a gentle learning curve”) by avoiding unnecessary deviation from C++, and instead focusing on subsetting the C++ feature. While we expect this feature to evolve somewhat, the changes we’re likely to want can easily be applied incrementally, and this is a fine starting point that anchors us on favoring syntax familiar to C++ developers.