Update generics goals doc (#3060)

This reflects changes from #2138 , plus a number of other updates,
including reflecting that we have decided to include templates in
Carbon.
This commit is contained in:
josh11b
2023-08-05 05:39:34 +00:00
committed by GitHub
parent ce459d4da7
commit 916bb4022a
2 changed files with 160 additions and 162 deletions
+159 -161
View File
@@ -14,13 +14,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Background](#background)
- [Generic parameters](#generic-parameters)
- [Interfaces](#interfaces)
- [Relationship to templates](#relationship-to-templates)
- [Goals](#goals)
- [Templates](#templates)
- [Checked-generic goals](#checked-generic-goals)
- [Use cases](#use-cases)
- [Generic programming](#generic-programming)
- [Upgrade path from C++ abstract interfaces](#upgrade-path-from-c-abstract-interfaces)
- [Dependency injection](#dependency-injection)
- [Generics instead of open overloading and ADL](#generics-instead-of-open-overloading-and-adl)
- [Checked generics instead of open overloading and ADL](#checked-generics-instead-of-open-overloading-and-adl)
- [Performance](#performance)
- [Better compiler experience](#better-compiler-experience)
- [Encapsulation](#encapsulation)
@@ -34,11 +34,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Interfaces are nominal](#interfaces-are-nominal)
- [Interop and evolution](#interop-and-evolution)
- [Bridge for C++ customization points](#bridge-for-c-customization-points)
- [What we are not doing](#what-we-are-not-doing)
- [What we are not doing with checked generics](#what-we-are-not-doing-with-checked-generics)
- [Not the full flexibility of templates](#not-the-full-flexibility-of-templates)
- [Template use cases that are out of scope](#template-use-cases-that-are-out-of-scope)
- [Generics will be checked when defined](#generics-will-be-checked-when-defined)
- [Specialization strategy](#specialization-strategy)
- [Checked generics will be checked when defined](#checked-generics-will-be-checked-when-defined)
- [Implementation strategy](#implementation-strategy)
- [References](#references)
<!-- tocstop -->
@@ -62,7 +61,8 @@ Carbon's checked generics will feature
[early type checking](terminology.md#early-versus-late-type-checking) and
[complete definition checking](terminology.md#complete-definition-checking).
Carbon's template generics, in contrast, will more closely follow the
Carbon's [template generics](#templates), in contrast, will more closely follow
the
[compile-time duck typing](https://en.wikipedia.org/wiki/Duck_typing#Templates_or_generic_types)
approach of C++ templates.
@@ -105,7 +105,7 @@ languages:
(compile-time only)
- [Abstract base classes](<https://en.wikipedia.org/wiki/Class_(computer_programming)#Abstract_and_concrete>)
in C++, etc. (run-time only)
- [Go interfaces](https://gobyexample.com/interfaces) (run-time only)
- [Go interfaces](https://gobyexample.com/interfaces)
In addition to specifying the methods available on a type, we may in the future
expand the role of interfaces to allow other type constraints, such as on size,
@@ -116,7 +116,7 @@ instead of, rather than in addition to, standard inheritance-and-classes
object-oriented language support. For the moment, everything beyond specifying
the _methods_ available is out of scope.
### Relationship to templates
## Templates
The entire idea of statically typed languages is that coding against specific
types and interfaces is a better model and experience. Unfortunately, templates
@@ -124,37 +124,37 @@ don't provide many of those benefits to programmers until it's too late, when
users are consuming the API. Templates also come with high overhead, such as
[template error messages](#better-compiler-experience).
We want Carbon code to move towards more rigorously type checked constructs.
We want Carbon code to move towards more rigorously type-checked constructs.
However, existing C++ code is full of unrestricted usage of compile-time
duck-typed templates. They are incredibly convenient to write and so likely will
continue to exist for a long time.
The question of whether Carbon has direct support for templates is out of scope
for this document. The generics design is not completely separate from
templates, so it is written as if Carbon will have its own templating system. It
is assumed to be similar to C++ templates with some specific changes:
Carbon will have direct support for templates in addition to checked generics.
Carbon's template system will be similar to C++ templates with some specific
changes:
- It may have some limitations to be more compatible with generics, much like
how we
[restrict overloading](#generics-instead-of-open-overloading-and-adl).
- It may have some limitations to be more compatible with checked generics,
much like how we
[restrict overloading](#checked-generics-instead-of-open-overloading-and-adl).
- We likely will have a different method of selecting between different
template instantiations, since
[SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)
makes it difficult to deliver high quality compiler diagnostics.
We assume Carbon will have templates for a few different reasons:
Other aspects of Carbon will reduce the number of situations where errors will
only be detected after the template is
[instantiated](terminology.md#instantiation):
- Carbon generics will definitely have to interact with _C++_ templates, and
many of the issues will be similar.
- We want to leave room in the design for templates, since it seems like it
would be easier to remove templates if they are not pulling their weight
than figure out how to add them in if they turn out to be needed.
- We may want to have templates in Carbon as a temporary measure, to make it
easier for users to transition off of C++ templates.
- Carbon's grammar won't require type information to disambiguate parsing, and
so definitions may be parsed without knowing the values of template
arguments.
- Carbon's name resolution is more restricted. Every package has its own
namespace, and there will be no
[argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl).
## Goals
## Checked-generic goals
Our goal for generics support in Carbon is to get most of the expressive
Our goal for checked generics support in Carbon is to get most of the expressive
benefits of C++ templates and open overloading with fewer downsides.
Additionally, we want to support some dynamic dispatch use cases; for example,
in cases that inheritance struggles with.
@@ -162,7 +162,7 @@ in cases that inheritance struggles with.
### Use cases
To clarify the expressive range we are aiming for, here are some specific use
cases we expect Carbon generics to cover.
cases we expect Carbon checked generics to cover.
#### Generic programming
@@ -183,18 +183,19 @@ generally be used with [static dispatch](#dispatch-control).
#### Upgrade path from C++ abstract interfaces
Interfaces in C++ are often represented by abstract base classes. Generics
should offer an alternative that does not rely on inheritance. This means looser
coupling and none of the problems of multiple inheritance. Some people, such as
Interfaces in C++ are often represented by abstract base classes. Checked
generics should offer an alternative that does not rely on inheritance. This
means looser coupling and none of the problems of multiple inheritance. Some
people, such as
[Sean Parent](https://sean-parent.stlab.cc/papers-and-presentations/#better-code-runtime-polymorphism),
advocate for runtime polymorphism patterns in C++ that avoid inheritance because
it can cause runtime performance, correctness, and code maintenance problems in
some situations. Those patterns require a lot of boilerplate and complexity in
C++. It would be nice if those patterns were simpler to express with Carbon
generics. More generally, Carbon generics will provide an alternative for those
situations inheritance doesn't handle as well. As a specific example, we would
like Carbon generics to supplant the need to support multiple inheritance in
Carbon.
checked generics. More generally, Carbon checked generics will provide an
alternative for those situations inheritance doesn't handle as well. As a
specific example, we would like Carbon checked generics to supplant the need to
support multiple inheritance in Carbon.
This is a case that would use [dynamic dispatch](#dispatch-control).
@@ -202,15 +203,16 @@ This is a case that would use [dynamic dispatch](#dispatch-control).
Types which only support subclassing for test stubs and mocks, as in
["dependency injection"](https://en.wikipedia.org/wiki/Dependency_injection),
should be able to easily migrate to generics. This extends outside the realm of
testing, allowing general configuration of how dependencies can be satisfied.
For example, generics might be used to configure how a library writes logs.
should be able to easily migrate to checked generics. This extends outside the
realm of testing, allowing general configuration of how dependencies can be
satisfied. For example, checked generics might be used to configure how a
library writes logs.
This would allow you to avoid the runtime overhead of virtual functions, using
[static dispatch](#dispatch-control) without the
[poor build experience of templates](#better-compiler-experience).
#### Generics instead of open overloading and ADL
#### Checked generics instead of open overloading and ADL
One name lookup problem we would like to avoid is caused by open overloading.
Overloading is where you provide multiple implementations of a function with the
@@ -226,20 +228,22 @@ function was originally defined. Together these enable
This is commonly used to provide a type-specific implementation of some
operation, but doesn't provide any enforcement of consistency across the
different overloads. It makes the meaning of code dependent on which overloads
are imported, and is at odds with being able to type check a function
generically.
are imported, and is at odds with being able to type check a function's
definition before instantiation.
Our goal is to address this use case, known more generally as
[the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions),
with a generics mechanism that does enforce consistency so that type checking is
possible without seeing all implementations. This will be Carbon's replacement
for open overloading. As a consequence, Carbon generics will need to be able to
support operator overloading.
with a checked-generics mechanism that does enforce consistency so that type
checking is possible without seeing all implementations. This will be Carbon's
replacement for open overloading. This is encapsulated in Carbon's
["one static open extension mechanism" principle](/docs/project/principles/static_open_extension.md).
As a consequence, Carbon checked generics will need to be able to support
operator overloading.
A specific example is the absolute value function `Abs`. We would like to write
`Abs(x)` for a variety of types. For some types `T`, such as `Int32` or
`Float64`, the return type will be the same `T`. For other types, such as
`Complex64` or `Quaternion`, the return type will be different. The generic
`Complex64` or `Quaternion`, the return type will be different. Checked generic
functions that call `Abs` will need a way to specify whether they only operate
on `T` such that `Abs` has signature `T -> T`.
@@ -250,14 +254,14 @@ overloading, which will
### Performance
For any real-world C++ template, there shall be an idiomatic reformulation in
Carbon generics that has equal or better performance.
Carbon checked generics that has equal or better performance.
[Performance is the top priority for Carbon](/docs/project/goals.md#performance-critical-software),
and we expect to use generics pervasively, and so they can't compromise that
goal in release builds.
and we expect to use checked generics pervasively, and so they can't compromise
that goal in release builds.
**Nice to have:** There are cases where we should aim to do better than C++
templates. For example, the additional structure of generics should make it
easier to reduce generated code duplication, reducing code size and cache
templates. For example, the additional structure of checked generics should make
it easier to reduce generated code duplication, reducing code size and cache
misses.
### Better compiler experience
@@ -266,12 +270,13 @@ Compared to C++ templates, we expect to reduce build times, particularly in
development builds. We also expect the compiler to be able to report clearer
errors, and report them earlier in the build process.
One source of improvement is that the bodies of generic functions and types can
be type checked once when they are defined, instead of every time they are used.
This is both a reduction in the total work done, and how errors can be reported
earlier. On use, the errors can be a lot clearer since they will be of the form
"argument did not satisfy function's contract as stated in its signature"
instead of "substitution failed at this line of the function's implementation."
One source of improvement is that the bodies of checked generic functions and
types can be type checked once when they are defined, instead of every time they
are used. This is both a reduction in the total work done, and how errors can be
reported earlier. On use, the errors can be a lot clearer since they will be of
the form "argument did not satisfy function's contract as stated in its
signature" instead of "substitution failed at this line of the function's
implementation."
**Nice to have:** In development builds, we will have the option of using
[dynamic dispatch](#dispatch-control) to reduce build times. We may also be able
@@ -284,31 +289,30 @@ arguments or identical implementations and only generating code for them once.
With a template, the implementation is part of the interface and types are only
checked when the function is called and the template is instantiated.
A generic function is type checked when it is defined, and type checking can't
use any information that is only known when the function is instantiated such as
the exact argument types. Furthermore, calls to a generic function may be type
checked using only its declaration, not its body. You should be able to call a
generic function using only a forward declaration.
A checked-generic function is type checked when it is defined, and type checking
can't use any information that is only known when the function is instantiated
such as the exact argument types. Furthermore, calls to a checked-generic
function may be type checked using only its declaration, not its body.
### Predictability
A general property of generics is they are more predictable than templates. They
make clear when a type satisfies the requirements of a function; they have a
documented contract. Further, that contract is enforced by the compiler, not
sensitive to implementation details in the function body. This eases evolution
by reducing (but not eliminating) the impact of
A general property of checked generics is they are more predictable than
templates. They make clear when a type satisfies the requirements of a function;
they have a documented contract. Further, that contract is enforced by the
compiler, not sensitive to implementation details in the function body. This
eases evolution by reducing (but not eliminating) the impact of
[Hyrum's law](https://www.hyrumslaw.com/).
**Nice to have:** We also want well-defined boundaries between what is legal and
not. This is "will my code be accepted by the compiler" predictability. We would
prefer to avoid algorithms in the compiler with the form "run for up to N steps
and report an error if it isn't resolved by then." For example, C++ compilers
will typically have a template recursion limit. With generics, these problems
arise due to trying to reason whether something is legal in all possible
instantiations, rather than with specific, concrete types.
will typically have a template recursion limit. With checked generics, these
problems arise due to trying to reason whether something is legal in all
possible instantiations, rather than with specific, concrete types.
Some of this is likely unavoidable or too costly to avoid, as most existing
generics systems
checked generics systems
[have undecidable aspects to their type system](https://3fx.ch/typing-is-hard.html),
including [Rust](https://sdleffler.github.io/RustTypeSystemTuringComplete/) and
[Swift](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). We
@@ -343,13 +347,13 @@ we should invest in putting the proof search into IDEs or other tooling.
Enable simple user control of whether to use dynamic or static dispatch.
**Implementation strategy:** There are two strategies for generating code for
generic functions:
checked-generic functions:
- Static specialization strategy: Like template parameters, the values for
generic parameters must be statically known at the callsite, or known to be
a generic parameter to the calling function. This can generate separate,
specialized versions of each combination of generic and template arguments,
in order to optimize for those types or values.
- Static strategy: Like template parameters, the values for checked parameters
must be statically known at the callsite, or known to be a generic parameter
to the calling function. This can generate separate, specialized versions of
each combination of checked and template arguments, in order to optimize for
those types or values.
- Dynamic strategy: This is when the compiler generates a single version of
the function that uses runtime dispatch to get something semantically
equivalent to separate instantiation, but likely with different size, build
@@ -363,17 +367,17 @@ code analysis, specific features used in the code, or profiling -- maybe some
specific specializations are needed for performance, but others would just be
code bloat.
We require that all generic functions can be compiled using the static
specialization strategy. For example, the values for generic parameters must be
We require that all checked generic functions can be compiled using the static
specialization strategy. For example, the values for checked parameters must be
statically known at the callsite. Other limitations are
[listed below](#specialization-strategy).
[listed below](#implementation-strategy).
**Nice to have:** It is desirable that the majority of functions with generic
**Nice to have:** It is desirable that the majority of functions with checked
parameters also support the dynamic strategy. Specific features may prevent the
compiler from using the dynamic strategy, but they should ideally be relatively
rare, and easy to identify. Language features should avoid making it observable
whether function code generated once or many times. For example, you should not
be able to take the address of a function with generic parameters, or determine
be able to take the address of a function with checked parameters, or determine
if a function was instantiated more than once using function-local static
variables.
@@ -382,12 +386,14 @@ limit the extent it is used automatically by implementations. For example, the
following features would benefit substantially from guaranteed monomorphization:
- Field packing in class layout. For example, packing a `bool` into the lower
bits of a pointer, or packing bit-fields with generic widths.
bits of a pointer, or packing bit-fields with checked-parameter widths.
- Allocating local variables in stack storage. Without monomorphization, we
would need to perform dynamic memory allocation -- whether on the stack or
the heap -- for local variables whose sizes depend on generic parameters.
the heap -- for local variables whose sizes depend on checked parameters.
- Passing parameters to functions. We cannot pass values of generic types in
registers.
- Finding [specialized](terminology.md#specialization) implementations of
interfaces beyond those clearly needed from the function signature.
While it is possible to address these with dynamic dispatch, handling some of
them might have far-reaching and surprising performance implications. We don't
@@ -407,53 +413,50 @@ acceptable even when running a development or debug build.
### Upgrade path from templates
We want there to be a natural, incremental upgrade path from templated code to
generic code.
[Assuming Carbon will support templates directly](#relationship-to-templates),
the first step of migrating C++ template code would be to first convert it to a
Carbon template. The problem is then how to convert templates to generics within
Carbon. This gives us these sub-goals:
checked-generic code. The first step of migrating C++ template code would be to
first convert it to a [Carbon template](#templates). The problem is then how to
convert templates to checked generics within Carbon. This gives us these
sub-goals:
- Users should be able to convert a single template parameter to be generic at
a time. A hybrid function with both template and generic parameters has all
- Users should be able to convert a single template parameter to be checked at
a time. A hybrid function with both template and checked parameters has all
the limitations of a template function: it can't be completely definition
checked, it can't use the dynamic strategy, etc. Even so, there are still
benefits from enforcing the function's declared contract for those
parameters that have been converted.
- Converting from a template parameter to a generic parameter should be safe.
- Converting from a template parameter to a checked parameter should be safe.
It should either work or fail to compile, never silently change semantics.
- We should minimize the effort to convert functions and types from templated
to generic. Ideally it should just require specifying the type constraints,
to checked. Ideally it should just require specifying the type constraints,
affecting just the signature of the function, not its body.
- **Nice to have:** It should be legal to call templated code from generic
code when it would have the same semantics as if called from non-generic
code, and an error otherwise. This is to allow more templated functions to
be converted to generics, instead of requiring them to be converted
specifically in bottom-up order.
- **Nice to have:** Provide a way to migrate from a template to a generic
without immediately updating all of the types used with the template. For
example, if the generic code requires types to implement a new interface,
one possible solution would use the original template code to provide an
implementation for that interface for any type that structurally has the
methods used by the original template.
If Carbon does not end up having direct support for templates, the transition
will necessarily be less incremental.
- **Nice to have:** It should be legal to call templated code from
checked-generic code when it would have the same semantics as if called from
non-generic code, and an error otherwise. This is to allow more templated
functions to be converted to checked generics, instead of requiring them to
be converted specifically in bottom-up order.
- **Nice to have:** Provide a way to migrate from a template to a checked
generic without immediately updating all of the types used with the
template. For example, if the checked-generic code requires types to
implement a new interface, one possible solution would use the original
template code to provide an implementation for that interface for any type
that structurally has the methods used by the original template.
### Path from regular functions
Replacing a regular, non-parameterized function with a generic function should
not affect existing callers of the function. There may be some differences, such
as when taking the address of the function, but ordinary calls should not see
any difference. In particular, the return type of a generic function should
match, without any type erasure or additional named members.
Replacing a regular, non-parameterized function with a checked-generic function
should not affect existing callers of the function. There may be some
differences, such as when taking the address of the function, but ordinary calls
should not see any difference. In particular, the return type of a
checked-generic function should match, without any type erasure or additional
named members.
### Coherence
We want the generics system to have the
[_coherence_ property](terminology.md#coherence), so that the implementation of
an interface for a type is well defined. Since a generic function only depends
on interface implementations, they will always behave consistently on a given
type, independent of context. For more on this, see
an interface for a type is well defined. Since a checked-generic function only
depends on interface implementations, they will always behave consistently on a
given type, independent of context. For more on this, see
[this description of what coherence is and why Rust enforces it](https://github.com/Ixrec/rust-orphan-rules#what-is-coherence).
Coherence greatly simplifies the language design, since it reduces the need for
@@ -479,7 +482,7 @@ cases remain:
- They should be some way of selecting between multiple implementations of an
interface for a given type. For example, a _Song_ might support multiple
orderings, such as by title or by artist. These would be represented by
having multiple implementations of a _Comparable_ interface.
having multiple implementations of a _Ordered_ interface.
- In order to allow libraries to be composed, there must be some way of saying
a type implements an interface that is in another package that the authors
of the type were unaware of. This is especially important since the library
@@ -490,7 +493,7 @@ We should have some mechanism for addressing these use cases. There are multiple
approaches that could work:
- Interface implementations could be external to types and are passed in to
generic functions separately.
checked-generic functions separately.
- There could be some way to create multiple types that are compatible with a
given value that you can switch between using casts to select different
interface implementations. This is the approach used by Rust
@@ -518,14 +521,15 @@ the definition of `T`.
### Learn from others
Many languages have implemented generics systems, and we should learn from those
experiences. We should copy what works and makes sense in the context of Carbon,
and change decisions that led to undesirable compromises. We are taking the
strongest guidance from Rust and Swift, which have similar goals and significant
experience with the implementation and usability of generics. They both use
nominal interfaces, were designed with generics from the start, and produce
native code. Contrast with Go which uses structural interfaces, or Java which
targets a virtual machine that predated its generics feature.
Many languages have implemented checked-generics systems, and we should learn
from those experiences. We should copy what works and makes sense in the context
of Carbon, and change decisions that led to undesirable compromises. We are
taking the strongest guidance from Rust and Swift, which have similar goals and
significant experience with the implementation and usability of checked
generics. They both use nominal interfaces, were designed with checked generics
from the start, and produce native code. Contrast with Go which uses structural
interfaces, or Java which targets a virtual machine that predated its generics
feature.
For example, Rust has found that supporting defaults for interface methods is a
valuable feature. It is useful for [evolution](#interop-and-evolution),
@@ -559,8 +563,8 @@ interface, even if those methods happen to have the same signature.
### Interop and evolution
[Evolution is a high priority for Carbon](/docs/project/goals.md#software-and-language-evolution),
and so will need mechanisms to support evolution when using generics. New
additions to an interface might:
and so will need mechanisms to support evolution when using checked generics.
New additions to an interface might:
- need default implementations
- be marked "upcoming" to allow for a period of transition
@@ -607,47 +611,39 @@ to work from C++, and Carbon functions could use that interface to invoke `swap`
on C++ types.
Similarly, we will want some way to implement Carbon interfaces for C++ types.
For example, we might have a template implementation of an `Addable` interface
For example, we might have a template implementation of an `AddWith` interface
for any C++ type that implements `operator+`.
## What we are not doing
## What we are not doing with checked generics
What are we **not** doing with generics, particularly things that some other
languages do?
What are we **not** doing with checked generics, particularly things that some
other languages do?
### Not the full flexibility of templates
Generics don't need to provide full flexibility of C++ templates:
Checked generics don't need to provide full flexibility of C++ templates:
- The current assumption is that
[Carbon templates](#relationship-to-templates) will cover those cases that
don't fit inside generics, such as code that relies on compile-time duck
typing.
- [Carbon templates](#templates) will cover those cases that don't fit inside
checked generics, such as code that relies on compile-time duck typing.
- We won't allow a specialization of some generic interface for some
particular type to actually expose a _different_ interface, with different
methods or different types in method signatures. This would break modular
type checking.
- [Template metaprogramming](https://en.wikipedia.org/wiki/Template_metaprogramming)
will not be supported by Carbon generics. We expect to address those use
cases with metaprogramming or templates in Carbon.
will not be supported by Carbon checked generics. We expect to address those
use cases with metaprogramming or [templates](#templates) in Carbon.
- [Expression templates](https://en.wikipedia.org/wiki/Expression_templates)
are out of scope. It may be possible to express this approach in Carbon's
checked-generics system, but they won't drive any accommodation in the
checked-generics design.
### Template use cases that are out of scope
We will also not require Carbon generics to support
[expression templates](https://en.wikipedia.org/wiki/Expression_templates),
[variadics](https://en.wikipedia.org/wiki/Variadic_function), or
[variadic templates](https://en.wikipedia.org/wiki/Variadic_template). Those are
all out of scope. It would be fine for our generics system to support these
features, but they won't drive any accommodation in the generics design, at
least until we have some resolution about templates in Carbon.
### Generics will be checked when defined
### Checked generics will be checked when defined
C++ compilers must defer full type checking of templates until they are
instantiated by the user. Carbon will not defer type checking of generic
instantiated by the user. Carbon will not defer type checking of checked-generic
definitions.
### Specialization strategy
### Implementation strategy
We want all generic Carbon code to support [static dispatch](#dispatch-control).
This means we won't support unbounded type families. Unbounded type families are
@@ -656,7 +652,7 @@ when recursion creates an infinite collection of types, such as in
or:
```carbon
fn Sort[T:! Comparable](list: List(T)) -> List(T) {
fn Sort[T:! Ordered](list: List(T)) -> List(T) {
if (list.size() == 1) return list;
var chunks: List(List(T)) = FormChunks(list, sqrt(list.size()));
chunks = chunks.ApplyToEach(Sort);
@@ -665,15 +661,14 @@ fn Sort[T:! Comparable](list: List(T)) -> List(T) {
}
```
This, given an implementation of `Comparable` for any list with elements that
are themselves `Comparable`, would recursively call itself to produce a set of
types without bound. That is, calling `Sort` on a `List(Int)` would internally
call `Sort` on a `List(List(Int))` and so on recursively without any static
limit.
This, given an implementation of `Ordered` for any list with elements that are
themselves `Ordered`, would recursively call itself to produce a set of types
without bound. That is, calling `Sort` on a `List(Int)` would internally call
`Sort` on a `List(List(Int))` and so on recursively without any static limit.
We won't require all generic Carbon code to support dynamic dispatch, but we
would like it to be an implementation option for the compiler in the majority of
cases.
We won't require all checked-generic Carbon code to support dynamic dispatch,
but we would like it to be an implementation option for the compiler in the
majority of cases.
Lastly, runtime specialization is out of scope as an implementation strategy.
That is, some language runtimes JIT a specialization when it is first needed,
@@ -681,5 +676,8 @@ but it is not a goal for Carbon to support such an implementation strategy.
## References
Proposals:
- [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24)
- [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950)
- [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138)
+1 -1
View File
@@ -55,7 +55,7 @@ Interfaces are Carbon's
[only static open extension mechanism](/docs/project/principles/static_open_extension.md),
so user-defined indexing must be defined in terms of interfaces. In order to
support
[definition checking of generic functions](/docs/design/generics/goals.md#generics-will-be-checked-when-defined),
[definition checking of generic functions](/docs/design/generics/goals.md#checked-generics-will-be-checked-when-defined),
it must be possible to fully typecheck indexing operations using only the
information exposed by those interfaces. And in order to provide
[coherence](/docs/design/generics/goals.md#coherence), a given indexing