From 9600030a0567a25c3ff3c83946eab2f31efd2695 Mon Sep 17 00:00:00 2001 From: josh11b Date: Wed, 30 Aug 2023 10:13:40 -0700 Subject: [PATCH] Reduce ambiguity in terminology (#3162) 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](https://github.com/carbon-language/carbon-lang/blob/trunk/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. --- docs/design/README.md | 41 +++--- docs/design/expressions/member_access.md | 2 +- docs/design/generics/terminology.md | 47 +++---- proposals/p3162.md | 157 +++++++++++++++++++++++ 4 files changed, 205 insertions(+), 42 deletions(-) create mode 100644 proposals/p3162.md diff --git a/docs/design/README.md b/docs/design/README.md index 50a6819e21d3..5fe416787005 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -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) @@ -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. @@ -688,18 +688,18 @@ 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 @@ -707,19 +707,23 @@ Value expressions are also broken down into three _value phases_: 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. @@ -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 @@ -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 diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index 5a53a763ca02..c3fb04292db5 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -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; } diff --git a/docs/design/generics/terminology.md b/docs/design/generics/terminology.md index dd1742d8aa00..f99582710777 100644 --- a/docs/design/generics/terminology.md +++ b/docs/design/generics/terminology.md @@ -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 @@ -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 @@ -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 @@ -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: diff --git a/proposals/p3162.md b/proposals/p3162.md new file mode 100644 index 000000000000..c0c6429b6f4d --- /dev/null +++ b/proposals/p3162.md @@ -0,0 +1,157 @@ +# Reduce ambiguity in terminology + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/3162) + + + +## Table of contents + +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + + + +## 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).