Jelajahi Sumber

Generics goals (#24)

The "generics" feature of Carbon is a large design effort that needs to be broken up into manageable steps. The first thing we need is a high-level goals document. The goals here reflect the desirable properties that we have discovered as part of considering several alternative generics designs:

-   Use cases:
    -   Generic programming
    -   Upgrade path from C++ abstract interfaces
    -   Dependency injection
     -   Generics instead of open overloading and ADL
-   Performance
-   Better compiler experience
-   Encapsulation
-   Predictability
-   Dispatch control
-   Upgrade path from templates
-   Coherence
-   No novel name lookup
-   Learn from others
-   Interfaces are nominal
-   Interop and evolution
-   Bridge for C++ customization points

Goals are summarized in [this presentation](https://docs.google.com/presentation/d/12yGyu5Pvdag7CJp-_yLVkmbhyvuRIilBTNlkO0LKaHo/edit?usp=sharing&resourcekey=0-JB9yrUO4-6J8-zzGhnIyNg).

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
Co-authored-by: Matthew Riley <mdriley@gmail.com>
Co-authored-by: austern <austern@google.com>
Co-authored-by: Dmitri Gribenko <gribozavr@gmail.com>
josh11b 5 tahun lalu
induk
melakukan
a6ddc03aa6

+ 17 - 0
docs/design/generics/README.md

@@ -0,0 +1,17 @@
+# Carbon: Generics
+
+<!--
+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
+-->
+
+This directory contains the collection of documents describing the generics
+feature of Carbon:
+
+-   ~~Overview~~ - not implemented yet
+-   [Goals](goals.md) - The motivation and principles guiding the design
+    direction.
+-   ~~Terminology~~ - not implemented yet
+-   ~~Detailed design~~ - not implemented yet
+-   ~~Rejected alternatives~~ - not implemented yet

+ 681 - 0
docs/design/generics/goals.md

@@ -0,0 +1,681 @@
+# Carbon: Generics goals
+
+<!--
+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
+-->
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Purpose of this document](#purpose-of-this-document)
+-   [Background](#background)
+    -   [Definition of generics](#definition-of-generics)
+    -   [Generic parameters](#generic-parameters)
+    -   [Interfaces](#interfaces)
+    -   [Relationship to templates](#relationship-to-templates)
+-   [Goals](#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)
+    -   [Performance](#performance)
+    -   [Better compiler experience](#better-compiler-experience)
+    -   [Encapsulation](#encapsulation)
+    -   [Predictability](#predictability)
+    -   [Dispatch control](#dispatch-control)
+    -   [Upgrade path from templates](#upgrade-path-from-templates)
+    -   [Coherence](#coherence)
+    -   [No novel name lookup](#no-novel-name-lookup)
+    -   [Learn from others](#learn-from-others)
+    -   [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)
+    -   [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)
+
+<!-- tocstop -->
+
+## Purpose of this document
+
+This document attempts to clarify our goals for the design of the generics
+feature for Carbon. While these are not strict requirements, they represent the
+yardstick by which we evaluate design decisions. We do expect to achieve most of
+these goals, though some of these goals are somewhat aspirational or
+forward-looking.
+
+## Background
+
+### Definition of generics
+
+**TODO** This should be replaced with a link to a terminology doc, once that is
+accepted.
+
+C++ supports
+[parametric polymorphism](https://en.wikipedia.org/wiki/Parametric_polymorphism)
+and [generic programming](https://en.wikipedia.org/wiki/Generic_programming)
+through templates. Templates use
+[compile-time duck typing](https://en.wikipedia.org/wiki/Duck_typing#Templates_or_generic_types)
+to decide whether arguments are valid. This is a form of
+[structural typing](https://en.wikipedia.org/wiki/Structural_type_system) that
+is usage based.
+
+Carbon will support _generics_ in order to allow definition checking in generic
+programming. Definition checking is accomplished by using
+[bounded parametric polymorphism](https://en.wikipedia.org/wiki/Parametric_polymorphism#Bounded_parametric_polymorphism)
+instead of compile-time duck typing. This means the legal arguments and the
+legal uses of a parameter are both goverened by explicit bounds on the parameter
+in a generic function's signature.
+
+This would be _in addition_ to
+[template support in Carbon](#relationship-to-templates), if we decide to
+support templates in Carbon beyond interoperability with C++ templates.
+
+### Generic parameters
+
+Generic functions and generic types will all take some "generic parameters",
+which will frequently be types, and in some cases will be implicit or inferred
+from the types of the values of explicit parameters.
+
+If a generic parameter is a type, the generic function's signature can specify
+constraints that the caller's type must satisfy. For example, a resizable array
+type (like C++'s `std::vector`) might have a generic type parameter with the
+constraint that the type must be movable and have a static size. A sort function
+might apply to any array whose elements are comparable and movable.
+
+A constraint might involve multiple generic parameters. For example, a merge
+function might apply to two arbitrary containers so long as their elements have
+the same type.
+
+### Interfaces
+
+We need some way to express the constraints on a generic type parameter. In
+Carbon we express these "type constraints" by saying we restrict to types that
+implement specific _interfaces_. Interfaces describe an API a type could
+implement; for example, it might specify a set of functions, including names and
+signatures. A type implementing an interface may be passed as a generic type
+argument to a function that has that interface as a requirement of its generic
+type parameter. Then, the functions defined in the interface may be called in
+the body of the function. Further, interfaces have names that allow them to be
+reused.
+
+Similar compile-time and run-time constructs may be found in other programming
+languages:
+
+-   [Rust's traits](https://doc.rust-lang.org/book/ch10-02-traits.html)
+-   [Swift's protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html)
+-   [Java interfaces](<https://en.wikipedia.org/wiki/Interface_(Java)>)
+-   [C++ concepts](<https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)>)
+    (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)
+
+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,
+prefix of the data layout, specified method implementations, tests that must
+pass, etc. This might be part of making interfaces as expressive as classes, as
+part of a strategy to migrate to a future version of Carbon that uses interfaces
+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
+
+The entire idea of statically typed languages is that coding against specific
+types and interfaces is a better model and experience. Unfortunately, templates
+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.
+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:
+
+-   It may have some limitations to be more compatible with generics, much like
+    how we [restrict overloading](#generics-instead-of-open-overloading).
+-   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:
+
+-   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.
+
+## Goals
+
+Our goal for 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.
+
+### Use cases
+
+To clarify the expressive range we are aming for, here are some specific use
+cases we expect Carbon generics to cover.
+
+#### Generic programming
+
+We in particular want to support
+[generic programming](https://en.wikipedia.org/wiki/Generic_programming),
+including:
+
+-   Containers: arrays, maps, lists, and more complicated data structures like
+    trees and graphs
+-   Algorithms: sort, search
+-   Wrappers: optional, variant, expected/result, smart pointers
+-   Parameterized numeric types: `std::complex<T>`
+-   Configurable and parametric APIs: the storage-customized `std::chrono` APIs
+-   [Policy-based design](https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design#Policy-based_design)
+
+These would generally involve static, compile-time type arguments, and so would
+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
+[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.
+
+This is a case that would use [dynamic dispatch](#dispatch-control).
+
+#### Dependency injection
+
+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.
+
+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
+
+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
+same name, and the implementation used in a specific context is determined by
+the argument types. Open overloading is overloading where the overload set is
+not restricted to a single file or library. This works with
+[Argument-dependent lookup](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup),
+or [ADL](https://en.cppreference.com/w/cpp/language/adl), a mechanism for
+enabling open overloading without having to reopen the namespace where the
+function was originally defined. Together these enable
+[C++ customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html).
+
+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.
+
+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.
+
+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
+functions that call `Abs` will need a way to specify whether they only operate
+on `T` such that `Abs` has signature `T -> T`.
+
+This does create an issue when interoperating with C++ code using open
+overloading, which will
+[need to be addressed](#bridge-for-c-customization-points).
+
+### Performance
+
+For any real-world C++ template, there shall be an idiomatic reformulation in
+Carbon 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.
+
+**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
+misses.
+
+### Better compiler experience
+
+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."
+
+**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
+to reduce the amount of redundant compilation work even with the
+[static strategy](#dispatch-control) by identifying instantiations with the same
+arguments or identical implementations and only generating code for them once.
+
+### Encapsulation
+
+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.
+
+### 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
+[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.
+
+Some of this is likely unavoidable or too costly to avoid, as most existing
+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
+fully expect there to be metaprogramming facilities in Carbon that will be able
+to execute arbitrary Turing machines, with infinite loops and undecidable
+stopping criteria. We don't see this as a problem though, just like we don't
+worry about trying to make the compiler reliably prevent you from writing
+programs that don't terminate.
+
+We _would_ like to distinguish "the executed steps are present in the program's
+source" from "the compiler has to search for a proof that the code is legal." In
+the former case, the compiler can surface a problem to the user by pointing to
+lines of code in a trace of execution. The user could employ traditional
+debugging techniques to refine their understanding until they can determine a
+fix. What we want to avoid is the latter case, since it has bad properties:
+
+-   Error messages end up in the form: "this was too complicated to figure out,
+    I eventually gave up."
+-   Little in the way of actionable feedback on how to fix problems.
+-   Not much the user can do to debug problems.
+-   If the compiler is currently right at a limit for figuring something out, it
+    is easy to imagine a change to a distant dependency can cause it to suddenly
+    stop compiling.
+
+If we can't find acceptable restrictions to make problems efficiently decidable,
+the next best solution is to require the proof to be in the source instead of
+derived by the compiler. If authoring the proof is too painful for the user, the
+we should invest in putting the proof search into IDEs or other tooling.
+
+### Dispatch control
+
+Enable simple user control of whether to use dynamic or static dispatch.
+
+**Implementation strategy:** There are two strategies for generating code for
+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.
+-   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
+    time, and performance characteristics.
+
+By default, we expect the implementation strategy to be controlled by the
+compiler, and not semantically visible to the user. For example, the compiler
+might use the static strategy for release builds and the dynamic strategy for
+development. Or it might choose between them on a more granular level based on
+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
+statically known at the callsite. Other limitations are
+[listed below](#specialization-strategy).
+
+**Nice to have:** It is desirable that the majority of functions with generic
+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
+if a function was instantiated more than once using function-local static
+variables.
+
+There are a few obstacles to supporting dynamic dispatch efficiently, which may
+limit the extent it is used automatically by implementations. For example, the
+following features would benefit substantially from guaranteed monomorphization:
+
+-   Field packing in struct layout. For example, packing a `Bool` into the lower
+    bits of a pointer, or packing bit-fields with generic 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.
+-   Passing parameters to functions. We cannot pass values of generic types in
+    registers.
+
+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
+want to compromise our goal for predictable performance.
+
+We will allow the user to explicitly opt-in to using the dynamic strategy in
+specific cases. This could be just to control binary size in cases the user
+knows are not performance sensitive, or it could be to get the additional
+capability of operating on values with dynamic types. We may need to restrict
+this in various ways to maintain efficiency, like Rust does with object-safe
+traits.
+
+We also anticipate that the user may want to force the compiler to use the
+static strategy in specific cases. This might be to keep runtime performance
+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:
+
+-   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
+    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.
+    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,
+    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.
+
+### Coherence
+
+We want the generics system to have the _coherence_ property. This means that
+there is a single answer to the question "what is the implementation of this
+interface for this type, if any?" independent of context, such as the libraries
+imported into a given file. 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
+[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
+complicated rules to picking an implementation when there are many candidates.
+It also has a number of benefits for users:
+
+-   It removes a way packages can conflict with each other.
+-   It makes the behavior of code more consistent and predictable.
+-   It means there is no need to provide a disambiguation mechanism.
+    Disambiguation is particularly problematic since the ambiguous call is often
+    in generic code rather than code you control.
+-   A consistent definition of a type is useful for instantiating a C++ or
+    Carbon template on that type.
+
+The main downside of coherence is that there are some capabilities we would like
+for interfaces which are in tension with the coherence property. For example, we
+would like to address
+[the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions#another-clojure-solution-using-protocols).
+We can get some of the way there by allowing the implementation of an interface
+for a type to be defined with either the interface or the type. But some use
+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.
+-   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
+    a type is defined in may not be able to see the interface definition without
+    creating a dependency cycle or layering violation.
+
+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.
+-   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
+    ([1](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types),
+    [2](https://github.com/Ixrec/rust-orphan-rules#user-content-why-are-the-orphan-rules-controversial)).
+-   Carbon could support
+    [scoped conformances](https://forums.swift.org/t/scoped-conformances/37159).
+
+### No novel name lookup
+
+We want to avoid adding rules for name lookup that are specific to generics.
+This is in contrast to Rust which has different lookup rules inside its traits.
+Instead, we should structure generics in a way that reuses existing name lookup
+facilities of the language.
+
+**Nice to have:** One application of this that would be nice to have is if the
+names of a type's members were all determined by a type's definition. So if `x`
+has type `T`, then if you write `x.y` you should be able to look up `y` in the
+definition of `T`. This might need to be somewhat indirect in some cases. For
+example, if `T` inherits from `U`, the name `y` might come from `U` and not be
+mentioned in the definition of `T` directly. We may have similar mechanisms
+where `T` gets methods that have default implementations in interfaces it
+implements, as long as the names of those interfaces are explicitly mentioned in
+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.
+
+For example, Rust has found that supporting defaults for interface methods is a
+valuable feature. It is useful for [evolution](#interop-and-evolution),
+implementation reuse, and for bridging the gap between the minimal functionality
+a type wants to implement and the rich API that users want to consume
+([example](https://doc.rust-lang.org/std/iter/trait.Iterator.html)).
+
+We still have the flexibility to make simplifications that Rust cannot because
+they need to maintain compatibility. We could remove the concept of
+`fundamental` and explicit control over which methods may be specialized. These
+are complicated and
+[impose coherence restrictions](http://aturon.github.io/tech/2017/02/06/specialization-and-coherence/).
+
+### Interfaces are nominal
+
+Interfaces can either be structural, as in Go, or nominal, as in Rust and Swift.
+Structural interfaces match any type that has the required methods, whereas
+nominal interfaces only match if there is an explicit declaration stating that
+the interface is implemented for that specific type. Carbon will support nominal
+interfaces, allowing them to designate _semantics_ beyond the basic structure of
+the methods.
+
+This means that interfaces implicitly specify the intended semantics and
+invariants of and between those functions. Unlike the function signatures, this
+contract is between the implementers and the consumers of interfaces and is not
+enforced by Carbon itself. For example, a `Draw` method would mean different
+things when it is part of a `GameResult` interface versus an `Image2D`
+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:
+
+-   need default implementations
+-   be marked "upcoming" to allow for a period of transition
+-   replace other APIs that need to be marked "deprecated"
+
+Experience with C++ concepts has shown that interfaces are
+[hard to evolve](https://www.youtube.com/watch?v=v_yzLe-wnfk) without these
+kinds of supporting language mechanisms. Otherwise changes to interfaces need to
+made simultaneously with updates to types that implement the interface or
+functions that consume it.
+
+Another way of supporting evolution is to allow one interface to be
+substitutable for another. For example, a feature that lets you use an
+implementation of `Interface1` for a type to automatically get an implementation
+of `Interface2`, as well as the other way around, would help transitioning
+between those two interfaces.
+
+Evolution in particular means that the set of names in an interface can change,
+and so two interfaces that don't start with name conflicts can develop them.
+
+To handle name conflicts, interfaces should be separate, isolated namespaces. We
+should provide mechanisms to allow one type to implement two interfaces that
+accidentally use the same name for different things, and for functions to use
+interfaces with name conflicts together on a single type. Contrast this with
+Swift, where a type can only supply one associated type of a given name even
+when implementing multiple protocols. Similarly a function in Swift with a given
+name and signature can only have a single implementation for a type.
+
+Note this is possible since [interfaces are nominal](#interfaces-are-nominal).
+The place where types specify that they implement an interface is also the
+vehicle for unambiguously designating which function implementation goes with
+what interface.
+
+### Bridge for C++ customization points
+
+There will need to be some bridge for C++ extension points that currently rely
+on open overloading or
+[ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup). For
+example, we need some way for C++
+[customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html)
+like `swap` to work on Carbon types. We might define `CPlusPlus.ADL.swap` as a
+Carbon interface to be that bridge. Carbon types could implement that interface
+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 any C++ type that implements `operator+`.
+
+## What we are not doing
+
+What are we **not** doing with 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:
+
+-   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.
+-   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.
+
+### 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
+
+C++ compilers must defer full type checking of templates until they are
+instantiated by the user. Carbon will not defer type checking of generic
+definitions.
+
+### Specialization 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
+when recursion creates an infinite collection of types, such as in
+[this example from Swift](https://forums.swift.org/t/ergonomics-generic-types-conforming-in-more-than-one-way/34589/71)
+or:
+
+```carbon
+fn Sort[Comparable T](List(T) list) -> List(T) {
+  if (list.size() == 1) return list;
+  var List(List(T)) chunks = FormChunks(list, sqrt(list.size()));
+  chunks = chunks.ApplyToEach(Sort);
+  chunks = Sort(chunks);
+  return MergeSortedListOfSortedLists(chunks);
+}
+```
+
+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.
+
+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.
+
+Lastly, runtime specialization is out of scope as an implementation strategy.
+That is, some language runtimes JIT a specialization when it is first needed,
+but it is not a goal for Carbon to support such an implementation strategy.

+ 1 - 0
proposals/README.md

@@ -22,6 +22,7 @@ request:
 <!-- proposals -->
 <!-- Generated by ./scripts/update_proposal_list.py -->
 
+-   [0024 - Generics goals](p0024.md)
 -   [0029 - Linear, rebase, and pull-request GitHub workflow](p0029.md)
 -   [0042 - Create code review guidelines](p0042.md)
 -   [0044 - Proposal tracking](p0044.md)

+ 38 - 0
proposals/p0024.md

@@ -0,0 +1,38 @@
+# Generics goals
+
+<!--
+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/24)
+
+## Problem
+
+The "generics" feature of Carbon is a large design effort that needs to be
+broken up into manageable steps. The first thing we need is a high-level goals
+document:
+
+-   to make sure we agree on the approach we are taking,
+-   to clearly communicate expectations to contributors for future steps,
+-   to encourage us to solve problems in the design in a consistent way,
+-   to provide a yardstick for measuring different alternatives under
+    consideration.
+
+## Background
+
+The question of what a generics feature would look like in Carbon has been an
+ongoing discussion, with many alternative proposals. Of course there have been a
+number of use cases that we want to address with this feature, but over the
+course of this process we have discovered a number of desirable properties we
+would like to achieve with any solution.
+
+## Proposal
+
+See the [generics goals document](/docs/design/generics/goals.md).
+
+## Rationale
+
+The goals here well reflect Carbon's goals as applied to the specifics of the
+generics feature.

+ 7 - 0
proposals/scripts/proposals_test.py

@@ -17,6 +17,13 @@ class TestProposal(unittest.TestCase):
         p = proposals.get_list(proposals_path)
         self.assertEqual(
             p[0],
+            (
+                "0024 - Generics goals",
+                "p0024.md",
+            ),
+        )
+        self.assertEqual(
+            p[1],
             (
                 "0029 - Linear, rebase, and pull-request GitHub workflow",
                 "p0029.md",