|
|
@@ -0,0 +1,241 @@
|
|
|
+# if/else
|
|
|
+
|
|
|
+<!--
|
|
|
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
|
+Exceptions. See /LICENSE for license information.
|
|
|
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
+-->
|
|
|
+
|
|
|
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/285)
|
|
|
+
|
|
|
+<!-- toc -->
|
|
|
+
|
|
|
+## 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)
|
|
|
+
|
|
|
+<!-- tocstop -->
|
|
|
+
|
|
|
+## Problem
|
|
|
+
|
|
|
+`if`/`else` is noted in the [language overview](/docs/design/README.md#ifelse),
|
|
|
+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](<https://en.wikipedia.org/wiki/Conditional_(computer_programming)>),
|
|
|
+seen in
|
|
|
+[many languages](<https://en.wikipedia.org/wiki/Conditional_(computer_programming)#Choice_system_cross_reference>).
|
|
|
+A few syntaxes that are likely to influence `if`/`else` are:
|
|
|
+
|
|
|
+- C++
|
|
|
+
|
|
|
+ ```cc
|
|
|
+ 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/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
|
|
|
+| /* pre-existing 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.
|