diff --git a/proposals/README.md b/proposals/README.md index 4f125d996828..7b4e61b8e175 100644 --- a/proposals/README.md +++ b/proposals/README.md @@ -61,5 +61,6 @@ request: - [0555 - Operator precedence](p0555.md) - [0601 - Operator tokens](p0601.md) - [0618 - var ordering](p0618.md) +- [0623 - Require braces](p0623.md) diff --git a/proposals/p0623.md b/proposals/p0623.md new file mode 100644 index 000000000000..b46f7f3f06be --- /dev/null +++ b/proposals/p0623.md @@ -0,0 +1,209 @@ +# Require braces + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/623) + + + +## Table of contents + +- [Problem](#problem) +- [Background](#background) + - [Consistency](#consistency) + - [C++ style rules](#c-style-rules) + - [`goto fail`](#goto-fail) +- [Proposal](#proposal) +- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) +- [Alternatives considered](#alternatives-considered) + - [Optional braces](#optional-braces) + - [Optional parentheses](#optional-parentheses) + - [`elif`](#elif) + + + +## Problem + +In C++, braces are often optional, such as in: + +``` +for (Shape x : shapes) + Draw(x); +``` + +Carbon adopted this design choice by default in proposals +[#285](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0285.md), +[#340](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0340.md), +and +[#353](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0353.md). +But should we keep it? + +## Background + +### Consistency + +We were adopting the C++ syntax primarily for easy consistency with C++. For +other languages: + +- Some modern languages require braces, including Go, Rust, and Swift. Note + some which require braces make parentheses optional. +- Kotlin is an example modern language which does not require braces. +- Older languages tend to make braces optional, including Java, JavaScript, + and C#. + +### C++ style rules + +There is a +[CERT rule](https://wiki.sei.cmu.edu/confluence/display/c/EXP19-C.+Use+braces+for+the+body+of+an+if%2C+for%2C+or+while+statement) +for when to use braces. + +### `goto fail` + +Apple had an infamous `goto fail` bug: + +- https://www.imperialviolet.org/2014/02/22/applebug.html +- https://dwheeler.com/essays/apple-goto-fail.html + +## Proposal + +Carbon should require braces when they might otherwise be optional. We should +continue to require parentheses. This currently comes up in `if`/`else`, +`while`, and `for`. + +We will allow `else if` as a special structure for repeated `if` statements due +to their frequency. For example, `if (...) { ... } else if (...) { ... }` +behaves identically to `if (...) { ... } else { if (...) { ... } }`. + +## Rationale based on Carbon's goals + +- **Software and language evolution**: Reducing parsing ambiguity of curly + braces is important for expanding the syntactic options for Carbon. + +- **Code that is easy to read, understand, and write**: We have a preference + to give only one way to do things, and optional braces are inconsistent with + that. It's also easier to understand and parse code that uses braces, and + defends against the possibility of `goto fail`-style bugs, whether + accidental or malicious. + +- **Interoperability with and migration from existing C++ code**: While C++ + does make braces optional in related situations, we believe this isn't + fundamental to migration or familiarity for C++ developers, so this goal is + not meaningfully impacted by this change. + +## Alternatives considered + +### Optional braces + +We could instead keep braces optional, such as: + +```carbon +if (x) + return y; +``` + +Advantages: + +- Consistent with C++. +- Allows for greater brevity in code, such as `if (!success) return error;`. + +Disadvantages: + +- Gives two ways of doing the same thing. + - Style guides will make choices, creating more contextual coding style. + - Some community members opined that requiring braces was the only + solution which is both appealing and easy to automate formatting for. + - A contrary opinion is that single-line if statements without braces + are more appealing, and not significantly more difficult to + automate. +- More complex and harder to parse. + + - Nested `if` statements can have unclear `else` bindings. For example: + + ```carbon + if (x) + if (y) + DoIfY(); + else + DoIfNotY(); + else + DoIfNotX(); + ``` + + - If Carbon were to reuse `if` and `else` keywords for a ternary operator, + that could omit braces in order to avoid ambiguity. For example, + `int x = if y then 3 else 7;`. + +- Developers are known to make mistakes adding statements to conditionals + missing braces, keeping consistent indentation, and missing the incorrect + behavior due to cognitive load. For example: + + ```carbon + if (x) + return y; + ``` + + -> + + ```carbon + if (x) + print("Returning y"); + return y; + ``` + + - Developers commonly want to add debugging statements to conditionals, + and missing braces can lead to these kinds of errors. + - It's possible that, over time, adding braces to add debug statements + then removing the debug statements without removing statements will lead + to braces remaining when braces are optional. + - This can be addressed by style guides and automated formatting that + inserts or removes braces as appropriate, cleaning up after + temporary edits. + - Some people feel the churn of adding braces and later removing them + is a significant toil that can be avoided by always adding them, + such as [here](https://wiki.c2.com/?AlwaysUseBracesOnIfThen). + - It can more easily lead to bugs like `goto fail;`. + - However, as the blog post points out, one can have incorrect + indentation with braces too. + +### Optional parentheses + +Languages which make braces required can make parentheses optional, or even +disallow them, such as: + +```carbon +if x { + return y; +} +``` + +Advantages: + +- Exchanges verbosity in syntax, requiring `{}` but removing `()`. + - Particularly useful in that many conditionals have `{}` regardless of + optionality, but all conditionals could in theory remove `()`. +- Cross-language consistency with languages that require `{}`. + +Disadvantages: + +- Visual inconsistency with C++. +- Parentheses make parsing less likely to encounter ambiguity. +- Optional parentheses lead to two ways of doing the same thing, although + disallowing them entirely could address this. + +### `elif` + +We could make `else if` a single token, such as `elseif` or `elif`. + +Advantages: + +- Can parse as a single token. + +Disadvantages: + +- Visual inconsistency with C++. + - `else if` also appears in some languages that require braces, such as + Go, Rust, and Swift.