# if/else [Pull request](https://github.com/carbon-language/carbon-lang/pull/285) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Executable semantics form](#executable-semantics-form) - [Caveats](#caveats) - [C++ as baseline](#c-as-baseline) - [if/else in expressions](#ifelse-in-expressions) - [Indentation](#indentation) - [Ambiguous else](#ambiguous-else) - [Alternatives considered](#alternatives-considered) - [No parentheses](#no-parentheses) - [Require braces](#require-braces) - [Rationale](#rationale) ## Problem `if`/`else` is noted in the [language overview](/docs/design/README.md), 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++ ```c++ if (x) { printf("x is true"); } else if (y) { printf("y is true"); } else { printf("Neither was true"); } ``` - Python ```python if x: print("x is true"); elif y: print("y is true"); else: print("Neither was true"); ``` - Swift ```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 makes `if` an expression, allowing: ```rust 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 ```bison 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](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). 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: ```carbon 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`: ```carbon 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](#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 rejects `if Type{.value = true}.value { thing1 } else { thing2 }` because it misinterprets the braces. - 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. 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 of `else`. - Braces avoid errors when adding statements. - For example, if: ```carbon if (x) do_parse = false; ``` has a statement added: ```carbon 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.