Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add C++-like while loops #340

Merged
merged 17 commits into from
Apr 21, 2021
1 change: 1 addition & 0 deletions proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ request:
- [0199 - String literals](p0199.md)
- [0199 - Decision](p0199_decision.md)
- [0253 - 2021 Roadmap](p0253.md)
- [0340 - while loops](p0340.md)

<!-- endproposals -->
262 changes: 262 additions & 0 deletions proposals/p0340.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# while loops

<!--
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/340)

<!-- 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)
- [`do`/`while`](#dowhile)
- [Declaring variables inside control flow conditions](#declaring-variables-inside-control-flow-conditions)
- [Alternatives considered](#alternatives-considered)
- [`else` block](#else-block)
- [Infinite loops](#infinite-loops)
- [Selection statements with initializer](#selection-statements-with-initializer)

<!-- tocstop -->

## Problem

`while` is noted in the [language overview](/docs/design/README.md#while), but
is provisional. Control flow is important, and `while` is basic; the form is
similar in many languages, even if details may change.

## Background

- C++: A couple example languages following C++'s syntax closely are Java and
TypeScript.

```cc
while (x) {
DoSomething();
}

do {
DoSomethingElse();
} while (y);
```

- Python: Python does not provide `do`/`while`. However, the `else` syntax for
having code execute if the condition is _never_ true may be of interest.

```python
while x:
DoSomething()

while True:
DoSomethingElse()
if not y:
break

while z:
print("z is true")
else:
print("z was never true")
```

- Swift: Swift uses `repeat` instead of `do`.

```swift
while x {
DoSomething()
}

repeat {
DoSomethingElse()
} while y
```

- Rust: Rust provides only a basic `while` loop, relying on the condition-less
`loop` to achieve `do`/`while`-like behavior.

```rust
while x {
DoSomething();
}

loop {
DoSomethingElse();
if (!y) { break; }
}
```

- Go: Go has no `while` loops, only `for` loops. However, a `for` can be
written similar to a `while`.

```go
for x {
DoSomething()
}

for {
DoSomethingElse();
if !y { break; }
}
```

## Proposal

Carbon should adopt `while` loop syntax consistent with C/C++. In particular:
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

- `while`: used in both `while` and `do`/`while` loops.
austern marked this conversation as resolved.
Show resolved Hide resolved
- `continue`: continues with the next loop iteration, starting with the loop
condition.
- `break`: breaks out of the loop, without testing the loop condition.

## Details

Loop syntax looks like:

- `while (` _boolean expression_ `) {` _statements_ `}`
austern marked this conversation as resolved.
Show resolved Hide resolved
austern marked this conversation as resolved.
Show resolved Hide resolved

While will evaluate the loop condition before each pass of the loop, only
continuing if the loop condition is true. When the loop condition evaluates to
false, the loop completes.

Similar to the
[`if`/`else` proposal](https://github.com/carbon-language/carbon-lang/pull/285),
the braces are optional and must be paired (`{ ... }`) if present. When there
are no braces, only one statement is allowed.

`continue` will continue with the next loop iteration directly, skipping any
other statements in the loop body. The next loop iteration behaves as normal,
starting with the condition being tested.

`break` exits the loop immediately, without testing the condition.

All of this is consistent with C/C++ behavior.

### Executable semantics form

```
%token WHILE
%token DO
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
%token CONTINUE
%token BREAK

statement:
WHILE '(' expression ')' statement
| CONTINUE ';'
| BREAK ';'
| /* pre-existing statements elided */
;
```

Note that `continue` and `break` should only be valid in a loop context.

## 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.

### `do`/`while`

`do`/`while` is omitted from this proposal because of disagreement about whether
it should be included in Carbon. It's better to have `do`/`while` considered
separately as a result, in order to separate review of the non-contentious
`while`.

### Declaring variables inside control flow conditions

C++ allows declaring a variable in the condition, such as:

```cc
while (optional<T> next = get_next()) { ... }
```

In Carbon, similar might be possible through expression syntax:

```carbon
while (var optional<T> next = get_next()) { ... }
```

However, in Carbon even more advanced cases might work through expression
syntax:

```carbon
while ((var Error<T> next = get_next()).Success()) { ... }
```

This would be generic expression functionality, and has implications outside of
`while`. As a result, syntax specific to `while` is not part of this proposal.

## Alternatives considered

Both alternatives from the
[`if`/`else` proposal](https://github.com/carbon-language/carbon-lang/pull/285)
apply to `while` as well: we could remove parentheses, require braces, or both.
The conclusions mirror here in order to avoid a divergence in syntax.

Additional alternatives follow.

### `else` block

Some languages support an `else` following `while`. This can support, for
example, running logic if the `while` condition never evaluated true, and so the
typical loop body has never run. For example, in Python:

```
while x:
print("x is true")
else:
print("x was *never* true")
```

This may be added later, but is not part of this proposal because it is not part
of C++ syntax.
chandlerc marked this conversation as resolved.
Show resolved Hide resolved

### Infinite loops

`while (true)` is a common idiom in C++ for an infinite loop. It may be useful
to add dedicated syntax for this, such as Rust's `loop`. However, this may be
addressed as part of a more generic looping solution that better addresses
complex situations (such as advanced use of C++'s `for (...; ...; ...)`).
Regardless, `loop` is not part of C++ syntax and so we should be cautious about
adding it.

### Selection statements with initializer

[Selections statements with initializer](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r1.html)
added `if (init; cond)` syntax to C++17. A corresponding `while (init; cond)` is
not proposed here, for several reasons:

- Although P0305 added this syntax to `if` and `switch`, it does not appear to
be in `while`. Its addition in this proposal would be a divergence from C++
which should probably be evaluated separately.

- There is some disagreement about the syntax used. For example, P0305
mentions a `with` keyword as an alternative, and there's some favor for
that. Particularly if Carbon omits the `for (init; cond; inc)` form that
`if (init; cond)` is structurally similar to, it may not make sense to do
`while (init; cond)`.

- It's possible that `if ((var status_code c = bar()) != SUCCESS)` will be
valid in Carbon, solving some of the concerns that led to `if (init; cond)`
syntax in a different way.

As a consequence, between not being in C++ and requiring more discussion,
`while (init; cond)` syntax is not part of this proposal.
austern marked this conversation as resolved.
Show resolved Hide resolved