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

Reduce ambiguity in terminology #3162

Merged
merged 5 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 23 additions & 18 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [String literals](#string-literals)
- [Values, objects, and expressions](#values-objects-and-expressions)
- [Expression categories](#expression-categories)
- [Value phases](#value-phases)
- [Expression phases](#expression-phases)
- [Composite types](#composite-types)
- [Tuples](#tuples)
- [Struct types](#struct-types)
Expand Down Expand Up @@ -399,7 +399,7 @@ Some values, such as `()` and `{}`, may even be used as types, but only act like
types when they are in a type position, like after a `:` in a variable
declaration or the return type after a `->` in a function declaration. Any
expression in a type position must be
[a constant or symbolic value](#value-phases) so the compiler can resolve
[a compile-time constant](#expression-phases) so the compiler can resolve
whether the value can be used as a type. This also puts limits on how much
operators can do different things for types. This is good for consistency, but
is a significant restriction on Carbon's design.
Expand Down Expand Up @@ -688,38 +688,42 @@ The primitive conversion steps used are:
> - Proposal
> [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006)

### Value phases
### Expression phases

Value expressions are also broken down into three _value phases_:
Value expressions are further broken down into three _expression phases_:

- A _constant_ has a value known at compile time, and that value is available
during type checking, for example to use as the size of an array. These
include literals ([integer](#integer-literals),
- A _template constant_ has a value known at compile time, and that value is
available during type checking, for example to use as the size of an array.
These include literals ([integer](#integer-literals),
[floating-point](#floating-point-literals), [string](#string-literals)),
concrete type values (like `f64` or `Optional(i32*)`), expressions in terms
of constants, and values of
[`template` parameters](#checked-and-template-parameters).
- A _symbolic value_ has a value that will be known at the code generation
- A _symbolic constant_ has a value that will be known at the code generation
stage of compilation when
[monomorphization](https://en.wikipedia.org/wiki/Monomorphization) happens,
but is not known during type checking. This includes
[checked-generic parameters](#checked-and-template-parameters), and type
expressions with checked-generic arguments, like `Optional(T*)`.
- A _runtime value_ has a dynamic value only known at runtime.

Carbon will automatically convert a constant to a symbolic value, or any value
to a runtime value:
Template constants together with symbolic constants are referred to as
_compile-time constants_.

Carbon will automatically convert a template constant to a symbolic constant, or
any value to a runtime value:

```mermaid
graph TD;
A(constant)-->B(symbolic value)-->C(runtime value);
A(template constant)-->B(symbolic constant)-->C(runtime value);
D(reference expression)-->C;
```

Constants convert to symbolic values and to runtime values. Symbolic values will
generally convert into runtime values if an operation that inspects the value is
performed on them. Runtime values will convert into constants or to symbolic
values if constant evaluation of the runtime expression succeeds.
Template constants convert to symbolic constants and to runtime values. Symbolic
constants will generally convert into runtime values if an operation that
inspects the value is performed on them. Runtime values will convert into
template or symbolic constants if constant evaluation of the runtime expression
succeeds.

> **Note:** Conversion of runtime values to other phases is provisional.

Expand Down Expand Up @@ -1006,7 +1010,7 @@ the program's correctness must not depend on which option the Carbon
implementation chooses.

A [generic binding](#checked-and-template-parameters) uses `:!` instead of a
colon (`:`) and can only match [constant or symbolic values](#value-phases), not
colon (`:`) and can only match [compile-time constant](#expression-phases), not
run-time values.

The keyword `auto` may be used in place of the type in a binding pattern, as
Expand Down Expand Up @@ -2723,8 +2727,9 @@ templates. Constraints can then be added incrementally, with the compiler
verifying that the semantics stay the same. Once all constraints have been
added, removing the word `template` to switch to a checked parameter is safe.

The [value phase](#value-phases) of a checked parameter is a symbolic value
whereas the value phase of a template parameter is constant.
The [expression phase](#expression-phases) of a checked parameter is a symbolic
constant whereas the expression phase of a template parameter is template
constant.

Although checked generics are generally preferred, templates enable translation
of code between C++ and Carbon, and address some cases where the type checking
Expand Down
2 changes: 1 addition & 1 deletion docs/design/expressions/member_access.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ fn CallStaticMethod(c: C) {

// ✅ OK
let T:! type = C.Nested;
// ❌ Error: value of `:!` binding is not constant because it
// ❌ Error: value of `:!` binding is not compile-time because it
// refers to local variable `c`.
let U:! type = c.Nested;
}
Expand Down
47 changes: 24 additions & 23 deletions docs/design/generics/terminology.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

Generally speaking, when we talk about _generics_, either
[checked or template](#checked-versus-template-parameters), we are talking about
generalizing some language construct by adding a compile-time parameter, called
a _generic parameter_, to it. So:
generalizing some language construct by adding a _compile-time parameter_, also
called a _generic parameter_, to it. So:

- a _generic function_ is a function with at least one compile-time parameter,
which could be an explicit argument to the function or
Expand Down Expand Up @@ -285,28 +285,28 @@ complete definition checking. This occurs for

_Binding patterns_ associate a name with a type and a value. This is used to
declare function parameters, in `let` and `var` declarations, as well as to
declare [generic parameters](#generic-means-compile-time-parameterized). There
are three kinds of binding patterns, corresponding to
[the three value phases](/docs/design/README.md#value-phases):
declare [compile-time parameters](#generic-means-compile-time-parameterized) for
classes, interfaces, and so on. There are three kinds of binding patterns,
corresponding to
[the three expression phases](/docs/design/README.md#expression-phases):

- A _runtime binding pattern_ binds to a dynamic value at runtime, and is
written using a `:`, as in `x: i32`.
- A _symbolic constant binding pattern_ or _symbolic binding pattern_ binds to
a compile-time value that is not known when type checking, and is used to
declare [checked generic](#checked-versus-template-parameters) parameters.
These binding use `:!`, as in `T:! type`.
- A _template constant binding pattern_ or _template binding pattern_ binds to
a compile-time value that is known when type checking, and is used to
declare [template](#checked-versus-template-parameters) parameters. These
bindings use the keyword `template` in addition to `:!`, as in
`template T:! type`.
- A _symbolic binding pattern_ binds to a compile-time value that is not known
when type checking, and is used to declare
[checked generic](#checked-versus-template-parameters) parameters. These
binding use `:!`, as in `T:! type`.
- A _template binding pattern_ binds to a compile-time value that is known
when type checking, and is used to declare
[template](#checked-versus-template-parameters) parameters. These bindings
use the keyword `template` in addition to `:!`, as in `template T:! type`.

The last two binding patterns, which are about binding a compile-time value, are
called _constant binding patterns_, and correspond to those binding patterns
called _compile-time binding patterns_, and correspond to those binding patterns
that use `:!`.

The name being declared, which is the identifier to the left of the `:` or `:!`,
is called a _binding_, or more specifically a _runtime binding_, _constant
is called a _binding_, or more specifically a _runtime binding_, _compile-time
binding_, _symbolic binding_, or _template binding_. The expression to the right
defining the type of the binding pattern is called the _binding type
expression_, a kind of [type expression](#type-expression). For example, in
Expand Down Expand Up @@ -368,10 +368,10 @@ cases, we are concerned with the type value after the implicit conversion.
## Facet binding

We use the term _facet binding_ to refer to the name introduced by a
[constant binding pattern](#bindings) (using `:!` with or without the `template`
modifier) where the declared type is a [facet type](#facet-type). In the binding
pattern `T:! Hashable`, `T` is a facet binding, and the value of `T` is a
[facet](#facet).
[compile-time binding pattern](#bindings) (using `:!` with or without the
`template` modifier) where the declared type is a [facet type](#facet-type). In
the binding pattern `T:! Hashable`, `T` is a facet binding, and the value of `T`
is a [facet](#facet).

## Deduced parameter

Expand Down Expand Up @@ -848,9 +848,10 @@ that specifies an array bound might have an integer type.

## Type constraints

Type constraints restrict which types are legal for generic parameters or
associated facets. They help define semantics under which they should be called,
and prevent incorrect calls.
Type constraints restrict which types are legal for a
[facet binding](#facet-binding), like a facet parameter or associated facet.
They help define semantics under which they should be called, and prevent
incorrect calls.

In general there are a number of different type relationships we would like to
express, for example:
Expand Down
157 changes: 157 additions & 0 deletions proposals/p3162.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Reduce ambiguity in terminology

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

<!-- toc -->

## Table of contents

- [Abstract](#abstract)
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)

<!-- tocstop -->

## Abstract

Change terminology away from terms that are ambiguous:

- Reserve "generic type" for types with (compile-time) parameters, like
`Vector` in `Vector(T:! type)`. Don't use that term to refer to `T`, as it
would with [#2360](/proposals/p2360.md#terminology).
- Use the term "compile-time" instead of "constant" to mean "template or
symbolic." Expand the term "constant" to include values, such as from `let`
bindings.

## Problem

Right now, the term "generic type" has two meanings. In this example:

```
class Vector(T:! type);
```

Both `Vector` and `T` could be called a "generic type." It would be much less
confusing if one of those two would have a different name.

Similarly, "constant" can currently mean multiple things:

- "evaluation at compile time," as in "constant evaluation"
- "not variable," as in `let` instead of `var`
- "non-mutating view," as in `const T*`

In practice, this has resulted in confusion. For example, the term "constant
bindings" doesn't include all `let` bindings, even though they are not variable
bindings.

## Background

The two meanings of "generic type" come from:

- Proposal [#2360](/proposals/p2360.md#terminology) defines a generic type to
be a type or facet introduced by a `:!` binding, such as in a generic
parameter or associated constant.
- Other uses of the term generic, such as in generic function, mean a language
construct with a compile-time parameter (as in
[Rust](https://doc.rust-lang.org/rust-by-example/generics.html)). This is
the usage in the broader programming language community, and includes
calling parameterized types "generic types" (as in
[Java](https://docs.oracle.com/javase/tutorial/java/generics/types.html),
[.NET](https://learn.microsoft.com/en-us/dotnet/standard/generics/#terminology),
[Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/#Generic-Types)).

Issue
[#1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391)
implemented in proposal
[#2964: Expression phase terminology](https://github.com/carbon-language/carbon-lang/pull/2964),
expanded the term "constant" from referring to just template constants to also
include symbolic constants from checked generics. Since then proposal
[#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006)
introduced the `const` modifier on types, providing a read-only view.

## Proposal

We make these changes:

- Reserve "generic type" for types with (compile-time) parameters, like
`Vector` in `Vector(T:! type)`. Don't use that term to refer to `T`, as it
would with
[#2360](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p2360.md#terminology).
- Expand "constant binding" to include all `let` bindings.
- Use the term "compile-time binding" to refer to the collection of template
and symbolic bindings, such as from generic parameters and associated
constants.
- Similarly "compile-time constants" to refer to template and symbolic
constants, as opposed to just "constants."
- The term "compile-time parameter" may be used instead of "generic
parameter." For now, both terms will be used, but in the future it might be
clearer to only use "generic" to mean "has compile-time parameters."
- Only use "symbolic binding" and "template binding" not "symbolic constant
binding" nor "template constant binding."
- Where applicable, switch from talking about parameters to bindings, since
almost everything that applies to compile-time parameters also applies to
compile-time bindings. For example, see
["binding patterns"](/docs/design/generics/terminology.md#bindings) and
["facet binding"](/docs/design/generics/terminology.md#facet-binding) in the
generics terminology doc.

## Details

The following design documents have been updated in this proposal to reflect
these changes:

- [Language design overview](/docs/design/README.md)
- [Generics terminology](/docs/design/generics/terminology.md)
- [Member access expressions](/docs/design/expressions/member_access.md)
**FIXME:** Need to sync with
[#3162](https://github.com/carbon-language/carbon-lang/pull/3162).

Some of these changes have already been implemented in:

- [PR #3048: Update Generics terminology document](https://github.com/carbon-language/carbon-lang/pull/3048)
- [PR #3061: Update generics overview](https://github.com/carbon-language/carbon-lang/pull/3061)

## Rationale

This proposal advances these goals of Carbon:

- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem):
Having clear and unambiguous terminology is important for making a precise
language specification and as well as other design and developer
documentation.
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write):
particularly the sub-goal that "The behavior and semantics of code should be
clearly and simply specified whenever possible."

## Alternatives considered

The main alternative considered was the status quo. This was discussed:

- [starting 2023-Jul-14 in #naming](https://discord.com/channels/655572317891461132/963846118964350976/1129542605538074777)
- [2023-Jul-27 in open discussion](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.3tki3lncihf)
- [2023-Aug-28 in #naming](https://discord.com/channels/655572317891461132/963846118964350976/1145790947083423777)

During those discussions, we also considered "symbolic type" instead of "generic
type". This did not work out, though, since it conflicted with the term
"symbolic" used as an expression phase. We considered other possibilities:
"figurative type", "computed type", "hole type", "open type", and "placeholder
type" (though that might be better applied to `auto`).

It also came up that we did not want to use the term "generic binding" to mean a
compile-time binding, because that term would be better applied to a
parameterized binding, see
[#naming on 2023-Jul-31](https://discord.com/channels/655572317891461132/963846118964350976/1135704682128494712).
Those are not currently supported in Carbon, but are something we are likely to
add. For example,
[Rust has generic associated types](https://rust-lang.github.io/generic-associated-types-initiative/explainer/motivation.html)
as of [v1.65](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html).