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

And, or, not #680

Merged
merged 8 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ request:
- [0601 - Operator tokens](p0601.md)
- [0618 - var ordering](p0618.md)
- [0623 - Require braces](p0623.md)
- [0680 - And, or, not](p0680.md)

<!-- endproposals -->
246 changes: 246 additions & 0 deletions proposals/p0680.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# And, or, not

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

<!-- toc -->

## Table of contents

- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Precedence](#precedence)
- [Associativity](#associativity)
- [Conversions](#conversions)
- [Overloading](#overloading)
- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
- [Alternatives considered](#alternatives-considered)
- [Precedence of AND versus OR](#precedence-of-and-versus-or)
- [Precedence of NOT](#precedence-of-not)
- [Punctuation form of NOT](#punctuation-form-of-not)
- [Repeated NOT](#repeated-not)

<!-- tocstop -->

## Problem

Logical AND, OR, and NOT are important building blocks for working with Boolean
values. Carbon should support them.

## Background

Most mainstream programming languages use one of two syntaxes for these
operators:

- `&&`, `||`, and `!`. The `&&` and `||` operators were
[first used by C](https://www.bell-labs.com/usr/dmr/www/chist.html) to
distinguish short-circuiting behavior from that of regular operators. They
are now seen in C, C++, C#, Swift, Rust, Haskell, and many more languages.
`&&` and `||` invariably short-circuit.
- `and`, `or`, and `not`. These are often used by languages with more emphasis
on readability, such as Python, Pascal, and various variants of BASIC. In
Python, the `and` and `or` operators short-circuit; in Pascal and Visual
Basic, they do not, but variants of them do:

- `and then` and `or else` in Pascal short-circuit.
- `AndAlso` and `OrElse` in Visual Basic short-circuit.

C++ recognizes `and`, `or`, and `not` as keywords and treats them as lexical
synonyms for `&&`, `||`, and `!`. C provides an `<iso646.h>` standard header
that exposes `and`, `or`, and `not` as macros expanding to `&&`, `||`, and
`!`.

The use of `&&` and `||` for short-circuiting logical operators is a common
source of error due to the potential for confusion with the bitwise `&` and `|`
operators. See:

- [CERT rule EXP46-C: Do not use a bitwise operator with a Boolean-like operand](https://wiki.sei.cmu.edu/confluence/display/c/EXP46-C.+Do+not+use+a+bitwise+operator+with+a+Boolean-like+operand)
- ChromiumOS bug prevents login due to `&&` / `&` typo
([news coverage](https://www.theregister.com/2021/07/23/chromebork_bug_google/)).

## Proposal

Carbon will provide three operators to support logical operations on Boolean
values.

- `and` provides a short-circuiting logical AND operation.
- `or` provides a short-circuiting logical OR operation.
- `not` provides a logical NOT operation.

## Details

`and` and `or` are infix binary operators. `not` is a prefix unary operator.

### Precedence

`and`, `or`, and `not` have very low precedence. When an expression appearing as
the condition of an `if` uses these operators unparenthesized, they are always
the lowest precedence operators in that expression.

These operators permit any reasonable operator that might be used to form a
boolean value as a subexpression. In particular, comparison operators such as
`<` and `==` have higher precedence than all logical operators.

A `not` operator can be used within `and` and `or`, but `and` cannot be used
directly within `or` without parentheses, nor the other way around.

For example:

```
if (n + m == 3 and not n < m) {
```

can be fully parenthesized as

```
if (((n + m) == 3) and (not (n < m))) {
```

and

```
if (cond1 == not cond2) {
// ...
if (cond1 and cond2 or cond3) {
```

are both errors, requiring parentheses.

### Associativity

`and` and `or` are left-associative. A `not` expression cannot be the operand of
another `not` expression -- `not not b` is an error.
zygoloid marked this conversation as resolved.
Show resolved Hide resolved

```
// OK
if (not a and not b and not c) { ... }
if (not (not a)) { ... }

// Error
if (not a or not b and not c) { ... }
if (not not a) { ... }
```

### Conversions

The operand of `and`, `or`, or `not` is converted to a Boolean value in the same
way as the condition of an `if` expression.
fowles marked this conversation as resolved.
Show resolved Hide resolved

### Overloading

The logical operators `and`, `or`, and `not` are not overloadable.

## Rationale based on Carbon's goals

- _Code that is easy to read, understand, and write:_

- The choice of `and` and `or` improves readability by avoiding the
potential for visual confusion between `&&` and `&`.
- The choice of `not` improves readability by avoiding the use of a small
and easy-to-miss punctuation character for logical negation.
- Having no precedence rule between `and` and `or` avoids readability
problems with expressions involving both operators, by requiring the use
of parentheses.
- The use of a keyword rather than punctuation for these operators helps
emphasize that they are not regular operators but instead have
control-flow semantics.
- Using the same precedence for `not` as for `and` and `or` allows
conditions to quickly be visually scanned and for the overall structure
and its nested conditions to be identified.

- _Interoperability with and migration from existing C++ code:_
- The use of the operators `and`, `or`, and `not`, which are keywords with
the same meaning in C++, avoids problems with a Carbon keyword colliding
with a valid C++ identifier and allows early adoption of the Carbon
syntax in C++ codebases.
- While these operators are overloadable in C++, they are nearly the
least-overloaded operators, and there is no known need for Carbon code
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
to call overloaded C++ `operator&&`, `operator||`, or `operator!`, nor
for Carbon code to provide such operators to C++.

## Alternatives considered

### Precedence of AND versus OR

Most languages give their AND operator higher precedence than their OR operator:

```c++
if (a && b || c && d) { ... }
// ... means ...
if ((a && b) || (c && d)) { ... }
```

This makes sense when viewed the right way: `&&` is the multiplication of
Boolean algebra and `||` is the addition, and multiplication usually binds
tighter than addition.

We could do the same. However, this precedence rule is not reliably known by a
significant fraction of developers despite having been present across many
languages for decades, leading to common recommendations to enable compiler
warnings for the first form in the above example, suggesting to rewrite it as
the second form. This therefore fails the test from proposal
[#555: when to add precedence edges](/proposals/p0555.md#when-to-add-precedence-edges).

### Precedence of NOT

The `not` operator has the same precedence rules as `and` and `or`. This gives a
simple rule, but may be inconvenient for some uses. For example:
chandlerc marked this conversation as resolved.
Show resolved Hide resolved

```
var x = cond1 == not cond2;
chandlerc marked this conversation as resolved.
Show resolved Hide resolved
```

is invalid and requires parentheses, and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why the above example on line 197 is ambiguous in a way that would lead to it being invalid, can you clarify this? I would've thought there's no operand other than not touching cond2 that could lead to an alternative interpretation for which precedence would be needed to resolve...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not is lower precedence than ==, so we can't have a not expression as a subexpression of an == expression following the precedence proposal. We could choose to have different rules on the left and right side of ==, so not a == b would mean not (a == b) but b == not a would mean b == (not a), but I think overall that would be too confusing, and it creates problems with things like a == not b == c, which would parse as a == (not (b == c)) using the normal operator precedence parser approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I still don't understand why this fails but not cond1 == cond2 should work (I would have thought that would be the ambiguous case indicative of an error).

Do what you like with this commentary, I don't think this is specific to the keyword spelling. At this point I'm not really asking for changes/replies, just observing that I don't really understand the intended behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I think this is something that will become even less ambiguous with grammar and the design document so not worried about getting it perfect in the proposal.


```
var y = not cond1 == cond2;
```

is equivalent to

```
var y = cond1 != cond2;
```

which may not be what the developer intended.

We could instead give the `not` operator high precedence, mirroring the behavior
in C++. However, this would break the symmetry between `and`, `or`, and `not`.

### Punctuation form of NOT

The syntax of the `not` operator may be harder to read than a `!` operator for
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
some uses. For example, when its operand is parenthesized, because it contains a
nested `and` or `or`:

```
if (not(thing1 and thing2) or
not(thing3 and thing4)) {
chandlerc marked this conversation as resolved.
Show resolved Hide resolved
```

Also, the spelling of the `!=` operator is hard to justify if there is no `!`
operator.

We could use the spelling `!` for this operator instead. Or, combined with the
previous alternative, we could introduce both a low-precedence `not` operator
and a high-precedence `!` operator. However, changing the syntax would break the
symmetry between `and`, `or`, and `not`, and including both operators would
either lead to one of the forms being unused or to the extra developer burden of
style rules suggesting when to use each.

### Repeated NOT

We could permit the syntax `not not x` as an idiom for converting `x` to a
Boolean type. However, we hope that a clearer syntax for that will be available,
such as perhaps `x as Bool`. In the presence of such syntax, `not not x` would
be an anti-pattern, and may indicate a bug due to an unintentionally repeated
`not` operator. Per
[#555: when to add precedence edges](/proposals/p0555.md#when-to-add-precedence-edges),
when in doubt, we omit the precedence rule and wait for real-world experience.