# 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)
-   [Rationale](#rationale)

<!-- tocstop -->

## 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](<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++

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