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

Unused Pattern Bindings (Unused Function Parameters) #2022

Merged
Merged
316 changes: 316 additions & 0 deletions proposals/p2022.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
# Unused Pattern Bindings (Unused Function Parameters)

<!--
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/2022

<!-- toc -->

## Table of contents

- [Abstract](#abstract)
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [The behavior of `unused` name bindings](#the-behavior-of-unused-name-bindings)
- [Examples](#examples)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)
- [Commented names](#commented-names)
- [Only short form support with `_`](#only-short-form-support-with-_)
- [Named identifiers prefixed with `_`](#named-identifiers-prefixed-with-_)
- [Anonymous, named identifiers](#anonymous-named-identifiers)
- [Attributes](#attributes)

<!-- tocstop -->

matthew-russo marked this conversation as resolved.
Show resolved Hide resolved
## Abstract

This proposal specifies how unused pattern bindings are written in the Carbon
programming language. This is a more general problem statement of "how do users
specify unused function parameters" as function parameter declarations are a
more specific form of pattern.

Related issue:
[#1996](https://github.com/carbon-language/carbon-lang/issues/1996).

## Problem

"How does a user of Carbon declare an unused pattern binding?"

Given that function parameters are a specific type of pattern binding, a more
specific question that will have the same answer is "How does a user of Carbon
declare an unused function parameter?"

Bindings that can be specified as unused makes code explicit and unambiguous.
Authors of code can clearly state that a value is not intended to be used. Tools
such as compilers or linters can explicitly handle a parameter that is specified
as unused, but later used, or a parameter that is unused despite not being
specified as unused.

## Background
matthew-russo marked this conversation as resolved.
Show resolved Hide resolved

See
[the overall design](https://github.com/carbon-language/carbon-lang/blob/09e4417431b77c05ceb2a98dd38833276514a1ff/docs/design/README.md#binding-patterns)
for the current state of things with regards to name bindings and the underscore
character, `_`.

See
[issue #476: Optional argument names (unused arguments)](https://github.com/carbon-language/carbon-lang/issues/476)
for the previous conversation on this topic.

Note: These are not exhaustive lists and not intended to be thoroughly
investigated, rather a brief overview of some prior art.

C and C++ style guides, linters, and other tools have addressed unused function
parameters specifically with varying levels of clarity for readers.

- [Standard C++ Foundation's guidelines on unused parameters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-unused)
- [Google's C++ style guide recommendations on unused parameters](https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions)
- [C++ `maybe_unused` attribute](https://en.cppreference.com/w/cpp/language/attributes/maybe_unused)
- [GCC C `unused` attribute](https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html#index-g_t_0040code_007bunused_007d-attribute_002e-2640)

More generally, various languages use the underscore character (`_`) to signal
an unused binding or pattern.

- [Rust `_` Patterns](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern)
- [Golang Blank Identifiers](https://go.dev/ref/spec#Blank_identifier)
- [PyLint allows parameters starting with `_`, `ignored_`, or `unused_` to be unused](https://pylint.pycqa.org/en/latest/user_guide/configuration/all-options.html#ignored-argument-names)
- [Scala Wildcard Patterns](https://www.scala-lang.org/files/archive/spec/2.11/10-pattern-matching.html#variable-patterns)
- [Crystal `_` Cases](https://crystal-lang.org/reference/1.5/syntax_and_semantics/case.html#underscore-allowed)

## Proposal
matthew-russo marked this conversation as resolved.
Show resolved Hide resolved

Users can explicitly declare a value in a pattern as unused in two forms:

- `_: i32` : Using the underscore token, `_`, in place of a name.

- `unused size: i32` : Using the leading `unused` keyword followed by the
name.

## Details

Introducing two syntaxes satisfies the desire to have a terse way to discard
values, but still provides authors with a more verbose, explicit syntax that
preserves the name.

Both approaches are unambiguous -- to human readers and authors as well as
programmatic interpretations. The inclusion of an explicit `unused` keyword
allows authors to preserve the name of a value for documentation purposes, while
still explicitly marking the value as discarded in an interpretable way to
humans and programs alike.
matthew-russo marked this conversation as resolved.
Show resolved Hide resolved

The `unused` keyword would be a new keyword in Carbon. This keyword would only
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
be valid when preceding a name in a pattern binding and the keyword would
tightly bind to the following name, disallowing specifying an entire sub-pattern
as `unused`.

### The behavior of `unused` name bindings

Names that are qualified with the `unused` keyword are visible for name lookup
but uses are invalid, including when they cause ambiguous name lookup errors. If
attempted to be used, a compiler error will be shown to the user, instructing
them to either remove the `unused` qualifier or remove the use.

The inverse, where a name is not qualified by the `unused` qualifier, but never
used, will cause the compiler to emit a warning diagnostic, informing the user
that a given name was not used and suggesting to either remove the binding or
mark it as unused.

### Examples
matthew-russo marked this conversation as resolved.
Show resolved Hide resolved

Examples with an unused function parameter

```carbon
// Function declaration (may be in API file)
fn Sum(x: List(i32), size: i32) -> i32;

// Implementation that doesn't use the size parameter
fn Sum(x: List(i32), _: i32) -> i32 { ... }
// or:
fn Sum(x: List(i32), unused size: i32) -> i32 { ... }
```

Examples with an unused variable

```carbon
fn Bar() -> (i32, i32);
fn Foo() -> i32 {
var (x: i32, _: i32) = Bar();
// or:
var (x: i32, unused y: i32) = Bar();

return x;
}
```

Examples with an unused case binding

```carbon
fn Bar() -> (i32, i32);
fn Foo() -> i32 {
match (Bar()) {
case (42, y: i32) => {
return y;
}
case (x: i32, _: i32) => {
return x;
}
// or:
case (x: i32, unused y: i32) => {
return x;
}
}
}
```

## Rationale

- This proposal supports the Carbon goal of having
[Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
- Carbon should not use symbols that are difficult to type, see, or
differentiate from similar symbols in commonly used contexts.
- Syntax should be easily parsed and scanned by any human in any
development environment, not just a machine or a human aided by semantic
hints from an IDE.
- Explicitness must be balanced against conciseness, as verbosity and
ceremony add cognitive overhead for the reader, while explicitness
reduces the amount of outside context the reader must have or assume.

This proposal uses the underscore, `_`, character to denote an unused value, a
meaning used across various other programming languages. A lone underscore
character only has a single meaning in Carbon and will be unambiguous across
contexts.

Both syntaxes are easily read by humans, either by seeing the `_` chacter alone,
or by introducing a keyword that allows code to read naturally such as
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
`unused size : i32`.

The inclusion of two syntaxes allows authors to decide when they will favor
conciseness or explicitness.

## Alternatives considered
Copy link
Contributor

Choose a reason for hiding this comment

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

Another rule that was considered was saying any parameter named starting with an _ is unused. See #476 (comment) . Please include the alternative and the rationale why it was not chosen (both what was stated in the issue and in https://github.com/carbon-language/carbon-lang/pull/2022/files#r945934384 ).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added an alternative for this -- reading over #476 it seemed the main argument against is that its "clever". Let me know if I should add something more

Copy link
Contributor

Choose a reason for hiding this comment

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

Should also include something about https://github.com/carbon-language/carbon-lang/pull/2022/files#r945934384 : "reserving" the name for later use (and avoiding accidents where there happens to be a matching name for lookup to find).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't include anything along those lines because if names prefixed with _ are discarded, there's nothing for them to ever conflict with or accidentally resolve to. Do you mean to include a pro that it can't lead to accidents?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is a possible advantage of the unused keyword approach that you can reserve a name, and a potential disadvantage of the _-prefix approach that you can't.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha -- thanks for the clarification. I'll update it this evening.

Copy link
Contributor

Choose a reason for hiding this comment

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

@chandlerc Maybe you could explain what was bad about being "clever" in #476 (comment) ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good call, suggested more specific wording below.


### Commented names

C++ allows function parameters to be unnamed, allowing function declarations
such as

```
int Foo(int) { ... }
int Foo(int /*unused_name*/) { ... }
```

Advantages:

- Consistency with C++

Disadvantages:

- Carbon does not intend to support `/* */` comments, so this option is
effectively a non-starter

### Only short form support with `_`

Carbon could provide only a single way to discard a value with the underscore,
`_`, token

```
// Function declaration (may be in API file)
fn Sum(x: List(i32), size: i32) -> i32;

// Implementation that doesn't use the size parameter
fn Sum(x: List(i32), _: i32) -> i32 { ... }
```

Advantages:

- A smaller language, one less keyword, and a single way to write code

Disadvantages:

- Less expressiveness, less documentation through names

### Named identifiers prefixed with `_`

Carbon could treat identifiers prefixed with `_` as unused identifiers and
discard their names.

```
// Function declaration (may be in API file)
fn Sum(x: List(i32), size: i32) -> i32;

// Implementation that doesn't use the size parameter
fn Sum(x: List(i32), _size: i32) -> i32 { ... }
```

Advantages:

- Reuse of the underscore character in both short and long form bindings
- Functionally similar to commented names in C++

Disadvantages:

- Tying the semantics of a name being unused to the particular spelling of the
identifier (starting with a `_` character), even while it remains an
identifier seems more subtle and indirect than necessary.
- We shouldn't use identifier spellings as a side-channel for semantic
information.
- Semantics, including used versus unused, should be conveyed in an
orthogonal manner to the name rather than tying them together.
- Would require the name to change when shifting between being used or unused,
which could undermine some of its utility such as remaining for annotations,
metaprogramming, or diagnostics.

### Anonymous, named identifiers

A potential syntax for naming unused bindings while retaining the underscore,
`_`, token is an optional name suffix following the underscore token.

```
// Function declaration (may be in API file)
fn Sum(x: List(i32), size: i32) -> i32;

// Implementation that doesn't use the size parameter
fn Sum(x: List(i32), _ size: i32) -> i32 { ... }
```

Advantages:

- Reuse of the underscore token in both short and long form bindings

Disadvantages:

- The underscore token, while consistent, may be less human readable than a
dedicated keyword such as `unused`

### Attributes

While attributes aren't designed yet, there's a possibility that in the future,
the Carbon language will have some mechanism to attach metadata to parts of
source code to inform readers, compilers, and other tools. Its conceivable that
there could be an `unused` attribute which could be use to implement similar
semantics as the proposed `unused` keyword.

Advantages:

- Opens the inspection and subsequent actions taken to whatever ecosystem
and programmatic support is introduced by attributes.
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
- Solves a specific problem with a generic approach
- Removes the introduction of a new keyword

Disadvantages:

- Given the current proposal where `unused` bindings specify unambiguous,
absolute behavior for the compiler's handling of names, similar to the
`private` keyword, using attributes as the transport for this semantic
information is indirect.

Given that attributes are not designed and may go in a number of unknown
directions, it might be worth revisiting this option once attributes are fully
designed.