Parcourir la source

Generics details 2: adapters, associated types, parameterized interfaces (#731)

This proposal goes into the details for these features of generics:

- adapters: for creating new types compatible with existing types but with different interface implementations
- associated types: allowing an interface implementation to specify some types to use in method signatures
- interface parameters: creating a family of interfaces, where types can implement more than one

This is a continuation of #553 . It has been summarized in these presentations:

- adapters: [1](https://docs.google.com/presentation/d/1bg6q0Q9Sk4YpRbNA3D3H34xYtaEO8ScAUNUZK2UTi80/edit?resourcekey=0-6-Y6e1mfRUmHg-Zk65Gc5A#slide=id.gcf40df1c7b_0_37) and [2](https://docs.google.com/presentation/d/17KG0TeJ4OChMRdLJPS8TE_K6SoL4lFy1FUGr2CDzX-A/edit?resourcekey=0-kLnZqd5NrbGSwmbunTyB-A#slide=id.g7a37009490_0_0)
- [associated types and interface parameters](https://docs.google.com/presentation/d/19hPpUjxQ0H1lUSLy5QjS2910Cpc7UdNKpF580fFsCGw/edit?resourcekey=0-ky9XGRC1I8X0Ffw6eqh7WQ#slide=id.p)

Co-authored-by: Wolff Dobson <wolffg@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
josh11b il y a 4 ans
Parent
commit
225eda7f49

+ 747 - 37
docs/design/generics/details.md

@@ -30,12 +30,20 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
         -   [Diamond dependency issue](#diamond-dependency-issue)
         -   [Diamond dependency issue](#diamond-dependency-issue)
     -   [Use case: overload resolution](#use-case-overload-resolution)
     -   [Use case: overload resolution](#use-case-overload-resolution)
 -   [Type compatibility](#type-compatibility)
 -   [Type compatibility](#type-compatibility)
+-   [Adapting types](#adapting-types)
+    -   [Adapter compatibility](#adapter-compatibility)
+    -   [Extending adapter](#extending-adapter)
+    -   [Use case: Using independent libraries together](#use-case-using-independent-libraries-together)
+    -   [Adapter with stricter invariants](#adapter-with-stricter-invariants)
+    -   [Application: Defining an impl for use by other types](#application-defining-an-impl-for-use-by-other-types)
+-   [Associated constants](#associated-constants)
+    -   [Associated class functions](#associated-class-functions)
+-   [Associated types](#associated-types)
+    -   [Model](#model-1)
+-   [Parameterized interfaces](#parameterized-interfaces)
+    -   [Impl lookup](#impl-lookup)
+    -   [Parameterized structural interfaces](#parameterized-structural-interfaces)
 -   [Future work](#future-work)
 -   [Future work](#future-work)
-    -   [Adapting types](#adapting-types)
-    -   [Associated constants](#associated-constants)
-    -   [Associated types](#associated-types)
-    -   [Parameterized interfaces](#parameterized-interfaces)
-        -   [Impl lookup](#impl-lookup)
     -   [Constraints](#constraints)
     -   [Constraints](#constraints)
     -   [Conditional conformance](#conditional-conformance)
     -   [Conditional conformance](#conditional-conformance)
     -   [Parameterized impls](#parameterized-impls)
     -   [Parameterized impls](#parameterized-impls)
@@ -183,13 +191,11 @@ interface Vector {
 }
 }
 ```
 ```
 
 
-The syntax here is to match how the same members would be defined in a type.
-Each declaration in the interface defines an _associated item_ (same
-[terminology as Rust](https://doc.rust-lang.org/reference/items/associated-items.html)).
-In this example, `Vector` has two associated methods, `Add` and `Scale`.
-
-**References:** Method syntax for types was decided in
-[question-for-leads issue #494](https://github.com/carbon-language/carbon-lang/issues/494).
+The syntax here is to match
+[how the same members would be defined in a type](/docs/design/classes.md#methods).
+Each declaration in the interface defines an
+[associated entity](terminology.md#associated-entity). In this example, `Vector`
+has two associated methods, `Add` and `Scale`.
 
 
 An interface defines a type-of-type, that is a type whose values are types. The
 An interface defines a type-of-type, that is a type whose values are types. The
 values of an interface are specifically
 values of an interface are specifically
@@ -203,7 +209,7 @@ interface.
 Carbon interfaces are ["nominal"](terminology.md#nominal-interfaces), which
 Carbon interfaces are ["nominal"](terminology.md#nominal-interfaces), which
 means that types explicitly describe how they implement interfaces. An
 means that types explicitly describe how they implement interfaces. An
 ["impl"](terminology.md#impls-implementations-of-interfaces) defines how one
 ["impl"](terminology.md#impls-implementations-of-interfaces) defines how one
-interface is implemented for a type. Every associated item is given a
+interface is implemented for a type. Every associated entity is given a
 definition. Different types satisfying `Vector` can have different definitions
 definition. Different types satisfying `Vector` can have different definitions
 for `Add` and `Scale`, so we say their definitions are _associated_ with what
 for `Add` and `Scale`, so we say their definitions are _associated_ with what
 type is implementing `Vector`. The `impl` defines what is associated with the
 type is implementing `Vector`. The `impl` defines what is associated with the
@@ -327,7 +333,7 @@ class GameBoard {
     fn Draw[me: Self]() { ... }
     fn Draw[me: Self]() { ... }
   }
   }
   impl as EndOfGame {
   impl as EndOfGame {
-    // Error: `GameBoard` has two methods named
+    // Error: `GameBoard` has two methods named
     // `Draw` with the same signature.
     // `Draw` with the same signature.
     fn Draw[me: Self]() { ... }
     fn Draw[me: Self]() { ... }
     fn Winner[me: Self](player: Int) { ... }
     fn Winner[me: Self](player: Int) { ... }
@@ -405,7 +411,7 @@ visible:
 ```
 ```
 var a: Point2 = (.x = 1.0, .y = 2.0);
 var a: Point2 = (.x = 1.0, .y = 2.0);
 // `a` does *not* have `Add` and `Scale` methods:
 // `a` does *not* have `Add` and `Scale` methods:
-// Error: a.Add(a.Scale(2.0));
+// Error: a.Add(a.Scale(2.0));
 
 
 // Cast from Point2 implicitly
 // Cast from Point2 implicitly
 var b: Point2 as Vector = a;
 var b: Point2 as Vector = a;
@@ -567,7 +573,7 @@ However, for another type implementing `Vector` but out-of-line using an
 
 
 ```
 ```
 fn AddAndScaleForPoint2(a: Point2, b: Point2, s: Double) -> Point2 {
 fn AddAndScaleForPoint2(a: Point2, b: Point2, s: Double) -> Point2 {
-  // ERROR: `Point2` doesn't have `Add` or `Scale` methods.
+  // ERROR: `Point2` doesn't have `Add` or `Scale` methods.
   return a.Add(b).Scale(s);
   return a.Add(b).Scale(s);
 }
 }
 ```
 ```
@@ -1144,7 +1150,7 @@ interface ConvertibleTo(T:! Type) { ... }
 
 
 // A type can only implement `PreferredConversion` once.
 // A type can only implement `PreferredConversion` once.
 interface PreferredConversion {
 interface PreferredConversion {
-  let AssociatedType: Type;
+  let AssociatedType:! Type;
   extends ConvertibleTo(AssociatedType);
   extends ConvertibleTo(AssociatedType);
 }
 }
 ```
 ```
@@ -1422,38 +1428,742 @@ var m: HashMap(String, Int);
 PrintValue(m, "key");
 PrintValue(m, "key");
 ```
 ```
 
 
-## Future work
-
-### Adapting types
+## Adapting types
 
 
 Since interfaces may only be implemented for a type once, and we limit where
 Since interfaces may only be implemented for a type once, and we limit where
 implementations may be added to a type, there is a need to allow the user to
 implementations may be added to a type, there is a need to allow the user to
-switch the type of a value to access different interface implementations. See
-["adapting a type" in the terminology document](terminology.md#adapting-a-type).
+switch the type of a value to access different interface implementations. We
+therefore provide a way to create new types
+[compatible with](terminology.md#compatible-types) existing types with different
+APIs, in particular with different interface implementations, by
+[adapting](terminology.md#adapting-a-type) them:
+
+```
+interface Printable {
+  fn Print[me: Self]();
+}
+interface Comparable {
+  fn Less[me: Self](that: Self) -> Bool;
+}
+class Song {
+  impl as Printable { fn Print[me: Self]() { ... } }
+}
+adapter SongByTitle for Song {
+  impl as Comparable {
+    fn Less[me: Self](that: Self) -> Bool { ... }
+  }
+}
+adapter FormattedSong for Song {
+  impl as Printable { fn Print[me: Self]() { ... } }
+}
+adapter FormattedSongByTitle for Song {
+  impl as Printable = FormattedSong as Printable;
+  impl as Comparable = SongByTitle as Comparable;
+}
+```
+
+This allows us to provide implementations of new interfaces (as in
+`SongByTitle`), provide different implementations of the same interface (as in
+`FormattedSong`), or mix and match implementations from other compatible types
+(as in `FormattedSongByTitle`). The rules are:
+
+-   You can add any declaration that you could add to a class except for
+    declarations that would change the representation of the type. This means
+    you can add functions, interface implementations, and aliases, but not
+    fields, base classes, or virtual functions.
+-   The adapted type is compatible with the original type, and that relationship
+    is an equivalence class, so all of `Song`, `SongByTitle`, `FormattedSong`,
+    and `FormattedSongByTitle` end up compatible with each other.
+-   Since adapted types are compatible with the original type, you may
+    explicitly cast between them, but there is no implicit casting between these
+    types (unlike between a type and one of its facet types / impls).
+-   For the purposes of generics, we only need to support adding interface
+    implementations. But this `adapter` feature could be used more generally,
+    such as to add methods.
+
+Inside an adapter, the `Self` type matches the adapter. Members of the original
+type may be accessed like any other facet type; either by a cast:
+
+```
+adapter SongByTitle for Song {
+  impl as Comparable {
+    fn Less[me: Self](that: Self) -> Bool {
+      return (this as Song).Title() < (that as Song).Title();
+    }
+  }
+}
+```
+
+or using qualified names:
+
+```
+adapter SongByTitle for Song {
+  impl as Comparable {
+    fn Less[me: Self](that: Self) -> Bool {
+      return this.(Song.Title)() < that(Song.Title)();
+    }
+  }
+}
+```
+
+**Open question:** As an alternative to:
+
+```
+impl as Printable = FormattedSong as Printable;
+```
+
+we could allow users to write:
+
+```
+impl as Printable = FormattedSong;
+```
+
+This would remove ceremony that the compiler doesn't need. The concern is
+whether it makes sense or is a category error. In this example, is
+`FormattedSong`, a type, a suitable value to provide when asking for a
+`Printable` implementation? An argument for this terser syntax is that the
+implicit conversion is legal in other contexts:
+
+```
+// ✅ Legal implicit conversion
+var v:! Printable = FormattedSong;
+```
+
+**Comparison with other languages:** This matches the Rust idiom called
+"newtype", which is used to implement traits on types while avoiding coherence
+problems, see
+[here](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types)
+and
+[here](https://github.com/Ixrec/rust-orphan-rules#user-content-why-are-the-orphan-rules-controversial).
+Rust's mechanism doesn't directly support reusing implementations, though some
+of that is provided by macros defined in libraries. Haskell has a
+[`newtype` feature](https://wiki.haskell.org/Newtype) as well. Haskell's feature
+doesn't directly support reusing implementations either, but the most popular
+compiler provides it as
+[an extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/newtype_deriving.html).
+
+### Adapter compatibility
+
+The framework from the [type compatibility section](#type-compatibility) allows
+us to evaluate when we can cast between two different arguments to a
+parameterized type. Consider three compatible types, all of which implement
+`Hashable`:
+
+```
+class Song {
+  impl as Hashable { ... }
+  impl as Printable { ... }
+}
+adapter SongHashedByTitle for Song {
+  impl as Hashable { ... }
+}
+adapter PlayableSong for Song {
+  impl as Hashable = Song as Hashable;
+  impl as Media { ... }
+}
+```
+
+Observe that `Song as Hashable` is different from
+`SongHashedByTitle as Hashable`, since they have different definitions of the
+`Hashable` interface even though they are compatible types. However
+`Song as Hashable` and `PlayableSong as Hashable` are almost the same. In
+addition to using the same data representation, they both implement one
+interface, `Hashable`, and use the same implementation for that interface. The
+one difference between them is that `Song as Hashable` may be implicitly cast to
+`Song`, which implements interface `Printable`, and `PlayableSong as Hashable`
+may be implicilty cast to `PlayableSong`, which implements interface `Media`.
+This means that it is safe to cast between
+`HashMap(Song, Int) == HashMap(Song as Hashable, Int)` and
+`HashMap(PlayableSong, Int) == HashMap(PlayableSong as Hashable, Int)` (though
+maybe only with an explicit cast) but
+`HashMap(SongHashedByTitle, Int) == HashMap(SongHashByTitle as Hashable, Int)`
+is incompatible. This is a relief, because we know that in practice the
+invariants of a `HashMap` implementation rely on the hashing function staying
+the same.
+
+### Extending adapter
+
+Frequently we expect that the adapter type will want to preserve most or all of
+the API of the original type. The two most common cases expected are adding and
+replacing an interface implementation. Users would indicate that an adapter
+starts from the original type's existing API by using the `extends` keyword
+instead of `for`:
+
+```
+class Song {
+  impl as Hashable { ... }
+  impl as Printable { ... }
+}
+
+adapter SongByArtist extends Song {
+  // Add an implementation of a new interface
+  impl as Comparable { ... }
+
+  // Replace an existing implementation of an interface
+  // with an alternative.
+  impl as Hashable { ... }
+}
+```
+
+The resulting type `SongByArtist` would:
+
+-   implement `Comparable`, unlike `Song`,
+-   implement `Hashable`, but differently than `Song`, and
+-   implement `Printable`, inherited from `Song`.
+
+Unlike the similar `class B extends A` notation, `adaptor B extends A` is
+permitted even if `A` is a final class. Also, there is no implicit conversion
+from `B` to `A`, matching `adapter`...`for` but unlike class extension.
+
+**Future work:** We may need additional mechanisms for changing the API in the
+adapter. For example, to resolve conflicts we might want to be able to move the
+implementation of a specific interface into an [external impl](#external-impl).
+
+### Use case: Using independent libraries together
+
+Imagine we have two packages that are developed independently. Package
+`CompareLib` defines an interface `CompareLib.Comparable` and a generic
+algorithm `CompareLib.Sort` that operates on types that implement
+`CompareLib.Comparable`. Package `SongLib` defines a type `SongLib.Song`.
+Neither has a dependency on the other, so neither package defines an
+implementation for `CompareLib.Comparable` for type `SongLib.Song`. A user that
+wants to pass a value of type `SongLib.Song` to `CompareLib.Sort` has to define
+an adapter that provides an implementation of `CompareLib.Comparable` for
+`SongLib.Song`. This adapter will probably use the
+[`extends` facility of adapters](#extending-adapter) to preserve the
+`SongLib.Song` API.
+
+```
+import CompareLib;
+import SongLib;
+
+adapter Song extends SongLib.Song {
+  impl as CompareLib.Comparable { ... }
+}
+// Or, to keep the names from CompareLib.Comparable out of Song's API:
+adapter Song extends SongLib.Song { }
+external impl Song as CompareLib.Comparable { ... }
+```
+
+The caller can either cast `SongLib.Song` values to `Song` when calling
+`CompareLib.Sort` or just start with `Song` values in the first place.
+
+```
+var lib_song: SongLib.Song = ...;
+CompareLib.Sort((lib_song as Song,));
+
+var song: Song = ...;
+CompareLib.Sort((song,));
+```
+
+### Adapter with stricter invariants
+
+**Future work:** Rust also uses the newtype idiom to create types with
+additional invariants or other information encoded in the type
+([1](https://doc.rust-lang.org/rust-by-example/generics/new_types.html),
+[2](https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction),
+[3](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html)).
+This is used to record in the type system that some data has passed validation
+checks, like `ValidDate` with the same data layout as `Date`. Or to record the
+units associated with a value, such as `Seconds` versus `Milliseconds` or `Feet`
+versus `Meters`. We should have some way of restricting the casts between a type
+and an adapter to address this use case.
+
+### Application: Defining an impl for use by other types
+
+Let's say we want to provide a possible implementation of an interface for use
+by types for which that implementation would be appropriate. We can do that by
+defining an adapter implementing the interface that is parameterized on the type
+it is adapting. That impl may then be pulled in using the `impl as ... = ...;`
+syntax.
+
+```
+interface Comparable {
+  fn Less[me: Self](that: Self) -> Bool;
+}
+adapter ComparableFromDifferenceFn
+    (T:! Type, Difference:! fnty(T, T)->Int) for T {
+  impl as Comparable {
+    fn Less[me: Self](that: Self) -> Bool {
+      return Difference(this, that) < 0;
+    }
+  }
+}
+class IntWrapper {
+  var x: Int;
+  fn Difference(this: Self, that: Self) {
+    return that.x - this.x;
+  }
+  impl as Comparable =
+      ComparableFromDifferenceFn(IntWrapper, Difference)
+      as Comparable;
+}
+```
+
+## Associated constants
+
+In addition to associated methods, we allow other kinds of
+[associated entities](terminology.md#associated-entity). For consistency, we use
+the same syntax to describe a constant in an interface as in a type without
+assigning a value. As constants, they are declared using the `let` introducer.
+For example, a fixed-dimensional point type could have the dimension as an
+associated constant.
+
+```
+interface NSpacePoint {
+  let N:! Int;
+  // The following require: 0 <= i < N.
+  fn Get[addr me: Self*](i: Int) -> Float64;
+  fn Set[addr me: Self*](i: Int, value: Float64);
+  // Associated constants may be used in signatures:
+  fn SetAll[addr me: Self*](value: Array(Float64, N));
+}
+```
+
+Implementations of `NSpacePoint` for different types might have different values
+for `N`:
+
+```
+class Point2D {
+  impl as NSpacePoint {
+    let N:! Int = 2;
+    fn Get[addr me: Self*](i: Int) -> Float64 { ... }
+    fn Set[addr me: Self*](i: Int, value: Float64) { ... }
+    fn SetAll[addr me: Self*](value: Array(Float64, 2)) { ... }
+  }
+}
+
+class Point3D {
+  impl as NSpacePoint {
+    let N:! Int = 3;
+    fn Get[addr me: Self*](i: Int) -> Float64 { ... }
+    fn Set[addr me: Self*](i: Int, value: Float64) { ... }
+    fn SetAll[addr me: Self*](value: Array(Float64, 3)) { ... }
+  }
+}
+```
+
+And these values may be accessed as members of the type:
+
+```
+Assert(Point2D.N == 2);
+Assert(Point3D.N == 3);
+
+fn PrintPoint[PointT:! NSpacePoint](p: PointT) {
+  for (var i: Int = 0; i < PointT.N; ++i) {
+    if (i > 0) { Print(", "); }
+    Print(p.Get(i));
+  }
+}
+
+fn ExtractPoint[PointT:! NSpacePoint](
+    p: PointT,
+    dest: Array(Float64, PointT.N)*) {
+  for (var i: Int = 0; i < PointT.N; ++i) {
+    (*dest)[i] = p.Get(i);
+  }
+}
+```
+
+**Comparison with other languages:** This feature is also called
+[associated constants in Rust](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants).
+
+**Aside:** In general, the use of `:!` here means these `let` declarations will
+only have compile-time and not runtime storage associated with them.
+
+### Associated class functions
+
+To be consistent with normal
+[class function](/docs/design/classes.md#class-functions) declaration syntax,
+associated class functions are written:
+
+```
+interface DeserializeFromString {
+  fn Deserialize(serialized: String) -> Self;
+}
+
+class MySerializableType {
+  var i: Int;
+
+  impl as DeserializeFromString {
+    fn Deserialize(serialized: String) -> Self {
+      return (.i = StringToInt(serialized));
+    }
+  }
+}
+
+var x: MySerializableType = MySerializableType.Deserialize("3");
+
+fn Deserialize(T:! DeserializeFromString, serialized: String) -> T {
+  return T.Deserialize(serialized);
+}
+var y: MySerializableType = Deserialize(MySerializableType, "4");
+```
+
+This is instead of declaring an associated constant using `let` with a function
+type.
+
+Together associated methods and associated class functions are called
+_associated functions_, much like together methods and class functions are
+called [member functions](/docs/design/classes.md#member-functions).
+
+## Associated types
+
+Associated types are [associated entities](terminology.md#associated-entity)
+that happen to be types. These are particularly interesting since they can be
+used in the signatures of associated methods or functions, to allow the
+signatures of methods to vary from implementation to implementation. We already
+have one example of this: the `Self` type discussed
+[above in the "Interfaces" section](#interfaces). For other cases, we can say
+that the interface declares that each implementation will provide a type under a
+specific name. For example:
+
+```
+interface StackAssociatedType {
+  let ElementType:! Type;
+  fn Push[addr me: Self*](value: ElementType);
+  fn Pop[addr me: Self*]() -> ElementType;
+  fn IsEmpty[addr me: Self*]() -> Bool;
+}
+```
+
+Here we have an interface called `StackAssociatedType` which defines two
+methods, `Push` and `Pop`. The signatures of those two methods declare them as
+accepting or returning values with the type `ElementType`, which any implementer
+of `StackAssociatedType` must also define. For example, maybe `DynamicArray`
+implements `StackAssociatedType`:
+
+```
+class DynamicArray(T:! Type) {
+  class IteratorType { ... }
+  fn Begin[addr me: Self*]() -> IteratorType;
+  fn End[addr me: Self*]() -> IteratorType;
+  fn Insert[addr me: Self*](pos: IteratorType, value: T);
+  fn Remove[addr me: Self*](pos: IteratorType);
+
+  impl as StackAssociatedType {
+    // Set the associated type `ElementType` to `T`.
+    let ElementType:! Type = T;
+    fn Push[addr me: Self*](value: ElementType) {
+      this->Insert(this->End(), value);
+    }
+    fn Pop[addr me: Self*]() -> ElementType {
+      var pos: IteratorType = this->End();
+      Assert(pos != this->Begin());
+      --pos;
+      returned var ret: ElementType = *pos;
+      this->Remove(pos);
+      return var;
+    }
+    fn IsEmpty[addr me: Self*]() -> Bool {
+      return this->Begin() == this->End();
+    }
+  }
+}
+```
+
+**Alternatives considered:** See
+[other syntax options considered for specifying associated types](/proposals/p0731.md#syntax-for-associated-constants).
+In particular, it was deemed that
+[Swift's approach of inferring the associated type from method signatures in the impl](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID190)
+was unneeded complexity.
 
 
-### Associated constants
+The definition of the `StackAssociatedType` is sufficient for writing a generic
+function that operates on anything implementing that interface, for example:
 
 
-In addition to associated methods, we will allow other kinds of associated items
-associating values with types implementing an interface.
+```
+fn PeekAtTopOfStack[StackType:! StackAssociatedType](s: StackType*)
+    -> StackType.ElementType {
+  var top: StackType.ElementType = s->Pop();
+  s->Push(top);
+  return top;
+}
+
+var my_array: DynamicArray(i32) = (1, 2, 3);
+// PeekAtTopOfStack's `StackType` is set to
+// `DynamicArray(i32) as StackAssociatedType`.
+// `StackType.ElementType` becomes `i32`.
+Assert(PeekAtTopOfStack(my_array) == 3);
+```
+
+Associated types can also be implemented using a
+[member type](/docs/design/classes.md#member-type).
+
+```
+interface Container {
+  let IteratorType:! Iterator;
+  ...
+}
 
 
-### Associated types
+class DynamicArray(T:! Type) {
+  ...
+  impl as Container {
+    class IteratorType { ... }
+    ...
+  }
+}
+```
+
+For context, see
+["Interface type parameters and associated types" in the generics terminology document](terminology.md#interface-type-parameters-versus-associated-types).
 
 
-Associated types are associated constants that happen to be types. These are
-particularly interesting since they can be used in the signatures of associated
-methods or functions, to allow the signatures of methods to vary from
-implementation to implementation.
+**Comparison with other languages:** Both
+[Rust](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types)
+and [Swift](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID189)
+support associated types.
 
 
-### Parameterized interfaces
+### Model
+
+The associated type can be modeled by a witness table field in the interface's
+witness table.
+
+```
+interface Iterator {
+  fn Advance[addr me: Self*]();
+}
+
+interface Container {
+  let IteratorType:! Iterator;
+  fn Begin[addr me: Self*]() -> IteratorType;
+}
+```
+
+is represented by:
+
+```
+class Iterator(Self:! Type) {
+  var Advance: fnty(this: Self*);
+  ...
+}
+class Container(Self:! Type) {
+  // Representation type for the iterator.
+  let IteratorType:! Type;
+  // Witness that IteratorType implements Iterator.
+  var iterator_impl: Iterator(IteratorType)*;
+
+  // Method
+  var Begin: fnty (this: Self*) -> IteratorType;
+  ...
+}
+```
+
+## Parameterized interfaces
 
 
 Associated types don't change the fact that a type can only implement an
 Associated types don't change the fact that a type can only implement an
-interface at most once. If instead you want a family of related interfaces, each
-of which could be implemented for a given type, you could use parameterized
-interfaces instead.
+interface at most once.
+
+If instead you want a family of related interfaces, one per possible value of a
+type parameter, multiple of which could be implemented for a single type, you
+would use parameterized interfaces. To write a parameterized version the stack
+interface instead of using associated types, write a parameter list after the
+name of the interface instead of the associated type declaration:
+
+```
+interface StackParameterized(ElementType:! Type) {
+  fn Push[addr me: Self*](value: ElementType);
+  fn Pop[addr me: Self*]() -> ElementType;
+  fn IsEmpty[addr me: Self*]() -> Bool;
+}
+```
+
+Then `StackParameterized(Fruit)` and `StackParameterized(Veggie)` would be
+considered different interfaces, with distinct implementations.
+
+```
+class Produce {
+  var fruit: DynamicArray(Fruit);
+  var veggie: DynamicArray(Veggie);
+  impl as StackParameterized(Fruit) {
+    fn Push[addr me: Self*](value: Fruit) {
+      this->fruit.Push(value);
+    }
+    fn Pop[addr me: Self*]() -> Fruit {
+      return this->fruit.Pop();
+    }
+    fn IsEmpty[addr me: Self*]() -> Bool {
+      return this->fruit.IsEmpty();
+    }
+  }
+  impl as StackParameterized(Veggie) {
+    fn Push[addr me: Self*](value: Veggie) {
+      this->veggie.Push(value);
+    }
+    fn Pop[addr me: Self*]() -> Veggie {
+      return this->veggie.Pop();
+    }
+    fn IsEmpty[addr me: Self*]() -> Bool {
+      return this->veggie.IsEmpty();
+    }
+  }
+}
+```
+
+Unlike associated types in interfaces and parameters to types, interface
+parameters can't be deduced. For example, if we were to rewrite
+[the `PeekAtTopOfStack` example in the "associated types" section](#associated-types)
+for `StackParameterized(T)` it would generate a compile error:
+
+```
+// ❌ Error: can't deduce interface parameter `T`.
+fn BrokenPeekAtTopOfStackParameterized
+    [T:! Type, StackType:! StackParameterized(T)]
+    (s: StackType*) -> T { ... }
+```
+
+This error is because the compiler can not determine if `T` should be `Fruit` or
+`Veggie` when passing in argument of type `Produce*`. The function's signature
+would have to be changed so that the value for `T` could be determined from the
+explicit parameters.
+
+```
+fn PeekAtTopOfStackParameterized
+    [T:! Type, StackType:! StackParameterized(T)]
+    (s: StackType*, _: singleton_type_of(T)) -> T { ... }
+
+var produce: Produce = ...;
+var top_fruit: Fruit =
+    PeekAtTopOfStackParameterized(&produce, Fruit);
+var top_veggie: Veggie =
+    PeekAtTopOfStackParameterized(&produce, Veggie);
+```
+
+The pattern `_: singleton_type_of(T)` is a placeholder syntax for an expression
+that will only match `T`, until issue
+[#578: Value patterns as function parameters](https://github.com/carbon-language/carbon-lang/issues/578)
+is resolved. Using that pattern in the explicit parameter list allows us to make
+`T` available earlier in the declaration so it can be passed as the argument to
+the parameterized interface `StackParameterized`.
+
+This approach is useful for the `ComparableTo(T)` interface, where a type might
+be comparable with multiple other types, and in fact interfaces for
+[operator overloads](#operator-overloading) more generally. Example:
+
+```
+interface EquatableWith(T:! Type) {
+  fn Equals[me: Self](that: T) -> Bool;
+  ...
+}
+class Complex {
+  var real: f64;
+  var imag: f64;
+  // Can implement this interface more than once as long as it has different
+  // arguments.
+  impl as EquatableWith(Complex) { ... }
+  impl as EquatableWith(f64) { ... }
+}
+```
+
+All interface parameters must be marked as "generic", using the `:!` syntax.
+This reflects these two properties of these parameters:
+
+-   They must be resolved at compile-time, and so can't be passed regular
+    dynamic values.
+-   We allow either generic or template values to be passed in.
+
+**Context:** See
+[interface type parameters](terminology.md#interface-type-parameters-versus-associated-types)
+in the terminology doc.
+
+**Note:** Interface parameters aren't required to be types, but that is the vast
+majority of cases. As an example, if we had an interface that allowed a type to
+define how the tuple-member-read operator would work, the index of the member
+could be an interface parameter:
+
+```
+interface ReadTupleMember(index:! u32) {
+  let T:! Type;
+  // Returns me[index]
+  fn Get[me: Self]() -> T;
+}
+```
+
+This requires that the index be known at compile time, but allows different
+indices to be associated with different types.
+
+**Caveat:** When implementing an interface twice for a type, you need to be sure
+that the interface parameters will always be different. For example:
+
+```
+interface Map(FromType:! Type, ToType:! Type) {
+  fn Map[addr me: Self*](needle: FromType) -> Optional(ToType);
+}
+class Bijection(FromType:! Type, ToType:! Type) {
+  impl as Map(FromType, ToType) { ... }
+  impl as Map(ToType, FromType) { ... }
+}
+// ❌ Error: Bijection has two impls of interface Map(String, String)
+var oops: Bijection(String, String) = ...;
+```
 
 
-#### Impl lookup
+In this case, it would be better to have an [adapting type](#adapting-types) to
+contain the `impl` for the reverse map lookup, instead of implementing the `Map`
+interface twice:
 
 
-We will have rules limiting where interface implementations are defined for
-coherence.
+```
+class Bijection(FromType:! Type, ToType:! Type) {
+  impl as Map(FromType, ToType) { ... }
+}
+adapter ReverseLookup(FromType:! Type, ToType:! Type)
+    for Bijection(FromType, ToType) {
+  impl as Map(ToType, FromType) { ... }
+}
+```
+
+**Comparison with other languages:** Rust calls
+[traits with type parameters "generic traits"](https://doc.rust-lang.org/reference/items/traits.html#generic-traits)
+and
+[uses them for operator overloading](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading).
+Note that Rust further supports defaults for those type parameters (such as
+`Self`).
+
+[Rust uses the term "type parameters"](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#clearer-trait-matching)
+for both interface type parameters and associated types. The difference is that
+interface parameters are "inputs" since they _determine_ which `impl` to use,
+and associated types are "outputs" since they are determined _by_ the `impl`,
+but play no role in selecting the `impl`.
+
+### Impl lookup
+
+Let's say you have some interface `I(T, U(V))` being implemented for some type
+`A(B(C(D), E))`. To satisfy the orphan rule for coherence, that `impl` must be
+defined in some library that must be imported in any code that looks up whether
+that interface is implemented for that type. This requires that `impl` is
+defined in the same library that defines the interface or one of the names
+needed by the type. That is, the `impl` must be defined with one of `I`, `T`,
+`U`, `V`, `A`, `B`, `C`, `D`, or `E`. We further require anything looking up
+this `impl` to import the _definitions_ of all of those names. Seeing a forward
+declaration of these names is insufficient, since you can presumably see forward
+declarations without seeing an `impl` with the definition. This accomplishes a
+few goals:
+
+-   The compiler can check that there is only one definition of any `impl` that
+    is actually used, avoiding
+    [One Definition Rule (ODR)](https://en.wikipedia.org/wiki/One_Definition_Rule)
+    problems.
+-   Every attempt to use an `impl` will see the exact same `impl`, making the
+    interpretation and semantics of code consistent no matter its context, in
+    accordance with the
+    [low context-sensitivity principle](/docs/project/principles/low_context_sensitivity.md).
+-   Allowing the `impl` to be defined with either the interface or the type
+    addresses the
+    [expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions).
+
+Note that [the rules for specialization](#lookup-resolution-and-specialization)
+do allow there to be more than one `impl` to be defined for a type, as long as
+one can unambiguously be picked as most specific.
+
+**References:** Implementation coherence is
+[defined in terminology](terminology.md#coherence), and is
+[a goal for Carbon](goals.md#coherence). More detail can be found in
+[this appendix with the rationale and alternatives considered](appendix-coherence.md).
+
+### Parameterized structural interfaces
+
+We should also allow the [structural interface](#structural-interfaces)
+construct to support parameters. Parameters would work the same way as for
+regular, that is nominal or non-structural, interfaces.
+
+## Future work
 
 
 ### Constraints
 ### Constraints
 
 

+ 101 - 16
docs/design/generics/overview.md

@@ -29,6 +29,10 @@ pointers to other design documents that dive deeper into individual topics.
     -   [Combining interfaces](#combining-interfaces)
     -   [Combining interfaces](#combining-interfaces)
         -   [Structural interfaces](#structural-interfaces)
         -   [Structural interfaces](#structural-interfaces)
         -   [Type erasure](#type-erasure)
         -   [Type erasure](#type-erasure)
+    -   [Adapting types](#adapting-types)
+    -   [Interface input and output types](#interface-input-and-output-types)
+        -   [Associated types](#associated-types)
+        -   [Parameterized interfaces](#parameterized-interfaces)
 -   [Future work](#future-work)
 -   [Future work](#future-work)
 
 
 <!-- tocstop -->
 <!-- tocstop -->
@@ -55,7 +59,7 @@ Summary of how Carbon generics work:
     They are used to avoid writing specialized, near-duplicate code for similar
     They are used to avoid writing specialized, near-duplicate code for similar
     situations.
     situations.
 -   Generics are written using _interfaces_ which have a name and describe
 -   Generics are written using _interfaces_ which have a name and describe
-    methods, functions, and other items for types to implement.
+    methods, functions, and other entities for types to implement.
 -   Types must explicitly _implement_ interfaces to indicate that they support
 -   Types must explicitly _implement_ interfaces to indicate that they support
     its functionality. A given type may implement an interface at most once.
     its functionality. A given type may implement an interface at most once.
 -   Implementations may be part of the type's definition, in which case you can
 -   Implementations may be part of the type's definition, in which case you can
@@ -140,8 +144,8 @@ requirements were sufficient.
 
 
 #### Defining interfaces
 #### Defining interfaces
 
 
-Interfaces, then, have a name and describe methods, functions, and other items
-for types to implement.
+Interfaces, then, have a name and describe methods, functions, and other
+entities for types to implement.
 
 
 Example:
 Example:
 
 
@@ -464,19 +468,100 @@ At that point, two erasures occur:
     of `PrintIt` you can cast a `CDCover as Printable` value back to `CDCover`.
     of `PrintIt` you can cast a `CDCover as Printable` value back to `CDCover`.
     Inside of `PrintIt`, you can't cast `p` or `T` back to `CDCover`.
     Inside of `PrintIt`, you can't cast `p` or `T` back to `CDCover`.
 
 
+### Adapting types
+
+Carbon has a mechanism called "adapting types" to create new types that are
+compatible with existing types but with different interface implementations.
+This could be used to add or replace implementations, or define implementations
+for reuse.
+
+In this example, we have multiple ways of sorting a collection of `Song` values.
+
+```
+class Song { ... }
+
+adapter SongByArtist extends Song {
+  impl as Comparable { ... }
+}
+
+adapter SongByTitle extends Song {
+  impl as Comparable { ... }
+}
+```
+
+Values of type `Song` may be cast to `SongByArtist` or `SongByTitle` to get a
+specific sort order.
+
+### Interface input and output types
+
+[Associated types and interface parameters](terminology.md#interface-type-parameters-and-associated-types)
+allow function signatures to vary with the implementing type. The biggest
+difference between these is that associated types ("output types") may be
+deduced from a type, and types can implement the same interface multiple times
+with different interface parameters ("input types").
+
+#### Associated types
+
+Expect types that vary in an interface to be associated types by default. Since
+associated types may be deduced, they are more convenient to use. Imagine a
+`Stack` interface. Different types implementing `Stack` will have different
+element types:
+
+```
+interface Stack {
+  let ElementType:! Movable;
+  fn Push[addr me: Self*](value: ElementType);
+  fn Pop[addr me: Self*]() -> ElementType;
+  fn IsEmpty[addr me: Self*]() -> Bool;
+}
+```
+
+`ElementType` is an associated type of the interface `Stack`. Types that
+implement `Stack` give `ElementType` a specific value of some type implementing
+`Movable`. Functions that accept a type implementing `Stack` can deduce the
+`ElementType` from the stack type.
+
+```
+// ✅ This is allowed, since the type of the stack will determine
+// `ElementType`.
+fn PeekAtTopOfStack[StackType:! Stack](s: StackType*)
+    -> StackType.ElementType;
+```
+
+#### Parameterized interfaces
+
+Parameterized interfaces are commonly associated with overloaded operators.
+Imagine an interface for determining if two values are equivalent that allows
+those types to be different. An element in a hash map might have type
+`Pair(String, i64)` that implements both `Equatable(String)` and
+`Equatable(Pair(String, i64))`.
+
+```
+interface Equatable(T:! Type) {
+  fn IsEqual[me: Self](compare_to: T) -> Bool;
+}
+```
+
+`T` is a parameter to interface `Equatable`. A type can implement `Equatable`
+multiple times as long as each time it is with a different value of the `T`
+parameter. Functions may accept types implementing `Equatable(i32)` or
+`Equatable(f32)`. Functions can't accept types implementing `Equatable(T)` in
+general, unless some other parameter determines `T`.
+
+```
+// ✅ This is allowed, since the value of `T` is determined by the
+// `v` parameter.
+fn FindInVector[T:! Type, U:! Equatable(T)](v: Vector(T), needle: U)
+    -> Optional(i32);
+
+// ❌ This is forbidden. Since `U` could implement `Equatable`
+// multiple times, there is no way to determine the value for `T`.
+// Contrast with `PeekAtTopOfStack` in the associated type example.
+fn CompileError[T:! Type, U:! Equatable(T)](x: U) -> T;
+```
+
 ## Future work
 ## Future work
 
 
--   Be able to have non-type generic parameters like the `UInt` size of an array
-    or tuple.
--   A "newtype" mechanism called "adapting types" may be provided to create new
-    types that are compatible with existing types but with different interface
-    implementations. This could be used to add or replace implementations, or
-    define implementations for reuse.
--   Associated types and interface parameters will be provided to allow function
-    signatures to vary with the implementing type. The biggest difference
-    between these is that associated types ("output types") may be deduced from
-    a type, and types can implement the same interface multiple times with
-    different interface parameters ("input types").
 -   Other kinds of constraints will be finalized.
 -   Other kinds of constraints will be finalized.
 -   Implementations can be parameterized to apply to multiple types. These
 -   Implementations can be parameterized to apply to multiple types. These
     implementations would be restricted to various conditions are true for the
     implementations would be restricted to various conditions are true for the
@@ -484,8 +569,8 @@ At that point, two erasures occur:
     specialization rule that picks the more specific one.
     specialization rule that picks the more specific one.
 -   Support functions should have a way to accept types that types that vary at
 -   Support functions should have a way to accept types that types that vary at
     runtime.
     runtime.
--   You should have the ability to mark items as `upcoming` or `deprecated` to
-    support evolution.
+-   You should have the ability to mark entities as `upcoming` or `deprecated`
+    to support evolution.
 -   Types should be able to define overloads for operators by implementing
 -   Types should be able to define overloads for operators by implementing
     standard interfaces.
     standard interfaces.
 -   There should be a way to provide default implementations of methods in
 -   There should be a way to provide default implementations of methods in

+ 98 - 33
docs/design/generics/terminology.md

@@ -24,6 +24,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 -   [Interface](#interface)
 -   [Interface](#interface)
     -   [Structural interfaces](#structural-interfaces)
     -   [Structural interfaces](#structural-interfaces)
     -   [Nominal interfaces](#nominal-interfaces)
     -   [Nominal interfaces](#nominal-interfaces)
+-   [Associated entity](#associated-entity)
 -   [Impls: Implementations of interfaces](#impls-implementations-of-interfaces)
 -   [Impls: Implementations of interfaces](#impls-implementations-of-interfaces)
 -   [Compatible types](#compatible-types)
 -   [Compatible types](#compatible-types)
 -   [Subtyping and casting](#subtyping-and-casting)
 -   [Subtyping and casting](#subtyping-and-casting)
@@ -39,7 +40,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     -   [Template specialization](#template-specialization)
     -   [Template specialization](#template-specialization)
     -   [Generic specialization](#generic-specialization)
     -   [Generic specialization](#generic-specialization)
 -   [Conditional conformance](#conditional-conformance)
 -   [Conditional conformance](#conditional-conformance)
--   [Interface type parameters versus associated types](#interface-type-parameters-versus-associated-types)
+-   [Interface type parameters and associated types](#interface-type-parameters-and-associated-types)
 -   [Type constraints](#type-constraints)
 -   [Type constraints](#type-constraints)
 -   [Type-of-type](#type-of-type)
 -   [Type-of-type](#type-of-type)
 
 
@@ -51,7 +52,7 @@ Generally speaking, when we talk about either templates or a generics system, we
 are talking about generalizing some language construct by adding a parameter to
 are talking about generalizing some language construct by adding a parameter to
 it. Language constructs here primarily would include functions and types, but we
 it. Language constructs here primarily would include functions and types, but we
 may want to support parameterizing other language constructs like
 may want to support parameterizing other language constructs like
-[interfaces](#interface-type-parameters-versus-associated-types).
+[interfaces](#interface-type-parameters-and-associated-types).
 
 
 This parameter broadens the scope of the language construct on an axis defined
 This parameter broadens the scope of the language construct on an axis defined
 by that parameter, for example it could define a family of functions instead of
 by that parameter, for example it could define a family of functions instead of
@@ -296,14 +297,30 @@ We use the "structural" versus "nominal" terminology as a generalization of the
 same terms being used in a
 same terms being used in a
 [subtyping context](https://en.wikipedia.org/wiki/Subtyping#Subtyping_schemes).
 [subtyping context](https://en.wikipedia.org/wiki/Subtyping#Subtyping_schemes).
 
 
+## Associated entity
+
+An _associated entity_ is a requirement in an interface that a type's
+implementation of the interface must satisfy by having a matching member. A
+requirement that the type define a value for a member constant is called an
+_associated constant_, and similarly an _associated function_ or _associated
+type_.
+
+Different types can satisfy an interface with different definitions for a given
+member. These definitions are _associated_ with what type is implementing the
+interface. An [impl](#impls-implementations-of-interfaces) defines what is
+associated with the type for that interface.
+
+Rust uses the term
+["associated item"](https://doc.rust-lang.org/reference/items/associated-items.html)
+instead of associated entity.
+
 ## Impls: Implementations of interfaces
 ## Impls: Implementations of interfaces
 
 
 An _impl_ is an implementation of an interface for a specific type. It is the
 An _impl_ is an implementation of an interface for a specific type. It is the
 place where the function bodies are defined, values for associated types, etc.
 place where the function bodies are defined, values for associated types, etc.
-are given. A given generics programming model may support default impls, named
-impls, or both. Impls are mostly associated with nominal interfaces; structural
-interfaces define conformance implicitly instead of by requiring an impl to be
-defined.
+are given. Impls are needed for [nominal interfaces](#nominal-interfaces);
+[structural interfaces](#structural-interfaces) define conformance implicitly
+instead of by requiring an impl to be defined.
 
 
 ## Compatible types
 ## Compatible types
 
 
@@ -514,18 +531,19 @@ that it always supports, but satisfies additional interfaces under some
 conditions on the type argument. For example: `Array(T)` might implement
 conditions on the type argument. For example: `Array(T)` might implement
 `Comparable` if `T` itself implements `Comparable`, using lexicographical order.
 `Comparable` if `T` itself implements `Comparable`, using lexicographical order.
 
 
-## Interface type parameters versus associated types
+## Interface type parameters and associated types
 
 
-Let's say you have an interface defining a container. Different containers will
-contain different types of values, and the container API will have to refer to
-that "element type" when defining the signature of methods like "insert" or
-"find". If that element type is a parameter (input) to the interface type, we
-say it is a type parameter; if it is an output, we say it is an associated type.
+Imagine an interface defining a container. Different containers will contain
+different types of values, and the container API will have to refer to that
+"element type" when defining the signature of methods like "insert" or "find".
+If that element type is a parameter (input) to the interface type, we say it is
+an _interface type parameter_; if it is an output, we say it is an _associated
+type_. An associated type is a kind of [associated entity](#associated-entity).
 
 
-Type parameter example:
+Interface type parameter example:
 
 
 ```
 ```
-interface Stack(ElementType:! Type)
+interface StackTP(ElementType:! Type)
   fn Push[addr me: Self*](value: ElementType);
   fn Push[addr me: Self*](value: ElementType);
   fn Pop[addr me: Self*]() -> ElementType;
   fn Pop[addr me: Self*]() -> ElementType;
 }
 }
@@ -534,8 +552,8 @@ interface Stack(ElementType:! Type)
 Associated type example:
 Associated type example:
 
 
 ```
 ```
-interface Stack {
-  let ElementType: Type;
+interface StackAT {
+  let ElementType:! Type;
   fn Push[addr me: Self*](value: ElementType);
   fn Push[addr me: Self*](value: ElementType);
   fn Pop[addr me: Self*]() -> ElementType;
   fn Pop[addr me: Self*]() -> ElementType;
 }
 }
@@ -551,33 +569,80 @@ interface Iterator { ... }
 interface Container {
 interface Container {
   // This does not make sense as an parameter to the container interface,
   // This does not make sense as an parameter to the container interface,
   // since this type is determined from the container type.
   // since this type is determined from the container type.
-  let IteratorType: Iterator;
+  let IteratorType:! Iterator;
   ...
   ...
   fn Insert[addr me: Self*](position: IteratorType, value: ElementType);
   fn Insert[addr me: Self*](position: IteratorType, value: ElementType);
 }
 }
 class ListIterator(ElementType:! Type) {
 class ListIterator(ElementType:! Type) {
   ...
   ...
-  impl Iterator;
+  impl as Iterator;
 }
 }
 class List(ElementType:! Type) {
 class List(ElementType:! Type) {
   // Iterator type is determined by the container type.
   // Iterator type is determined by the container type.
-  let IteratorType: Iterator = ListIterator(ElementType);
+  let IteratorType:! Iterator = ListIterator(ElementType);
   fn Insert[addr me: Self*](position: IteratorType, value: ElementType) {
   fn Insert[addr me: Self*](position: IteratorType, value: ElementType) {
     ...
     ...
   }
   }
-  impl Container;
+  impl as Container;
 }
 }
 ```
 ```
 
 
-Since type parameters are directly under the user's control, it is easier to
-express things like "this type parameter is the same for all these interfaces",
-and other type constraints.
+If you have an interface with type parameters, a type can have multiple impls
+for different combinations of type parameters. As a result, type parameters may
+not be deduced in a function call. However, if the interface parameters are
+specified, a type can only have a single implementation of the given interface.
+This unique implementation choice determines the values of associated types.
 
 
-If you have an interface with type parameters, there is a question of whether a
-type can have multiple impls for different combinations of type parameters, or
-if you can only have a single impl (in which case you can directly infer the
-type parameters given just a type implementing the interface). You can always
-infer associated types.
+For example, we might have an interface that says how to perform addition with
+another type:
+
+```
+interface Addable(T:! Type) {
+  let ResultType:! Type;
+  fn Add[me: Self](rhs: T) -> ResultType;
+}
+```
+
+An `i32` value might support addition with `i32`, `u16`, and `f64` values.
+
+```
+impl i32 as Addable(i32) {
+  let ResultType:! Type = i32;
+  // ...
+}
+impl i32 as Addable(u16) {
+  let ResultType:! Type = i32;
+  // ...
+}
+impl i32 as Addable(f64) {
+  let ResultType:! Type = f64;
+  // ...
+}
+```
+
+To write a generic function requiring a parameter to be `Addable`, there needs
+to be some way to determine the type to add to:
+
+```
+// ✅ This is allowed, since the value of `T` is determined by the
+// `y` parameter.
+fn DoAdd[T:! Type, U:! Addable(T)](x: U, y: T) -> U.ResultType {
+  return x.Add(y);
+}
+
+// ❌ This is forbidden, can't uniquely determine `T`.
+fn CompileError[T:! Type, U:! Addable(T)](x: U) -> T;
+```
+
+Once the interface parameter can be determined, that determines the values for
+associated types, such as `ResultType` in the example. As always, calls with
+types for which no implementation exists will be rejected at the call site:
+
+```
+// ❌ This is forbidden, no implementation of `Addable(Orange)`
+// for `Apple`.
+DoAdd(apple, orange);
+```
 
 
 ## Type constraints
 ## Type constraints
 
 
@@ -595,12 +660,12 @@ express, for example:
     element type.
     element type.
 -   An interface may define an associated type that needs to be constrained to
 -   An interface may define an associated type that needs to be constrained to
     implement some interfaces.
     implement some interfaces.
--   This type parameter must be [compatible](#compatible-types) with another
-    type. You might use this to define alternate implementations of a single
-    interfaces, such as sorting order, for a single type.
+-   This type must be [compatible](#compatible-types) with another type. You
+    might use this to define alternate implementations of a single interfaces,
+    such as sorting order, for a single type.
 
 
-Note that type constraints can be a restriction on one type parameter, or can
-define a relationship between multiple type parameters.
+Note that type constraints can be a restriction on one type parameter or
+associated type, or can define a relationship between multiple types.
 
 
 ## Type-of-type
 ## Type-of-type
 
 

+ 1 - 0
proposals/README.md

@@ -68,5 +68,6 @@ request:
 -   [0676 - `:!` generic syntax](p0676.md)
 -   [0676 - `:!` generic syntax](p0676.md)
 -   [0680 - And, or, not](p0680.md)
 -   [0680 - And, or, not](p0680.md)
 -   [0722 - Nominal classes and methods](p0722.md)
 -   [0722 - Nominal classes and methods](p0722.md)
+-   [0731 - Generics details 2: adapters, associated types, parameterized interfaces](p0731.md)
 
 
 <!-- endproposals -->
 <!-- endproposals -->

+ 384 - 0
proposals/p0731.md

@@ -0,0 +1,384 @@
+# Generics details 2: adapters, associated types, parameterized interfaces
+
+<!--
+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/731)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Problem](#problem)
+-   [Background](#background)
+-   [Proposal](#proposal)
+-   [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
+-   [Alternatives considered](#alternatives-considered)
+    -   [`adaptor` instead of `adapter`](#adaptor-instead-of-adapter)
+    -   [Syntax for associated constants](#syntax-for-associated-constants)
+        -   [Omitting types](#omitting-types)
+        -   [Inferring associated types from method signatures](#inferring-associated-types-from-method-signatures)
+    -   [Value patterns](#value-patterns)
+    -   [Deduced interface parameters](#deduced-interface-parameters)
+        -   [Rationale for the rejection](#rationale-for-the-rejection)
+            -   [Impl lookup rules with deducible interface parameters](#impl-lookup-rules-with-deducible-interface-parameters)
+    -   [Only associated types, no interface parameters](#only-associated-types-no-interface-parameters)
+    -   [Others](#others)
+
+<!-- tocstop -->
+
+## Problem
+
+We want to Carbon to have a high quality generics feature that achieves the
+goals set out in [#24](https://github.com/carbon-language/carbon-lang/pull/24).
+This is too big to land in a single proposal. This proposal continues
+[#553](https://github.com/carbon-language/carbon-lang/pull/553) defining the
+details of:
+
+-   adapters
+-   associated types and other constants
+-   parameterized interfaces
+
+## Background
+
+This is a follow on to these previous generics proposals:
+
+-   [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24)
+-   [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447)
+-   [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524)
+-   [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553)
+
+The content for this proposal was extracted from a larger
+[Generics combined draft proposal](https://github.com/carbon-language/carbon-lang/pull/36).
+
+## Proposal
+
+This is a proposal to add multiple sections to
+[this design document on generics details](/docs/design/generics/details.md).
+
+## Rationale based on Carbon's goals
+
+Much of this rationale was captured in the
+[Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24).
+
+## Alternatives considered
+
+### `adaptor` instead of `adapter`
+
+We considered replacing the `adapter` keyword with the alternate spelling of
+"adaptor". Both spellings can be used for the intended meaning, but the "-er"
+spelling is more common in English text and in code. The final deciding factor
+was that the
+[GoF Design Patterns book](https://en.wikipedia.org/wiki/Design_Patterns) spells
+the ["adapter pattern"](https://en.wikipedia.org/wiki/Adapter_pattern) with the
+["-er" spelling](https://springframework.guru/gang-of-four-design-patterns/adapter-pattern/).
+
+### Syntax for associated constants
+
+Issue
+[#739: Associated type syntax](https://github.com/carbon-language/carbon-lang/issues/739)
+decided that the syntax for assigning a value to an associated constant, such as
+an associated type.
+
+The decision was to use `let` with `:!` to express that these are compile-time
+values, matching the use in classes described in proposal
+[#772](p0722.md#let-constants).
+
+```
+interface Stack {
+  let ElementType:! Type;
+  fn Push[addr me: Self*](value: ElementType);
+  ...
+}
+
+class DynamicArray(T:! Type) {
+  ...
+  impl as Stack {
+    let ElementType:! Type = T;
+    fn Push[addr me: Self*](value: ElementType);
+    ...
+  }
+}
+```
+
+One advantage was this opened the door for a type to satisfy the associated
+types of two interfaces with the same name with a single `let` declaration using
+constraints satisfying the requirements of both interfaces.
+
+This type can be replaced with `auto`, to have it determined automatically.
+
+```
+class DynamicArray(T:! Type) {
+  ...
+  impl as Stack {
+    let ElementType:! auto = T;
+    fn Push[addr me: Self*](value: ElementType);
+    ...
+  }
+}
+```
+
+This would avoid needing to change the `impl` when the constraints in the
+interface changed as long as the value to the right of the `=` satisfied the new
+constraints. Otherwise if the constraints are being weakened, first functions
+relying on the capabilities being removed would have to change, then they would
+be changed in the interface, and finally the implementations for types. If the
+constraints are being strengthened, the implementations for types would have to
+change first followed by the interface.
+
+#### Omitting types
+
+We also considered omitting the type in the `impl`, always using the type
+declared in the interface.
+
+```
+class DynamicArray(T:! Type) {
+  ...
+  impl as Stack {
+    let ElementType = T;
+    fn Push[addr me: Self*](value: ElementType);
+    ...
+  }
+}
+```
+
+This would provide the advantage of reducing the number of changes when changing
+the constraint specified in the interface. If the constraints were being
+weakened, then functions that used the capability that was being removed would
+break or need to be modified. If the constraints were being strengthened, then
+only type implementations that didn't satisfy the new constraints would break or
+need to be modified.
+
+The biggest difference from the selected option is when adding a constraint. In
+that case the selected option would have more churn, because all implementations
+would be updated even if they already satisfied the new constraint. This comes
+with the advantage of making it easier _incrementally enforce_ greater
+constraints.
+
+On the whole, it seems like both could be made to work. You could explicitly
+specify constraints with this option by using an alias to a normal
+`let ..:! TypeOfType` declaration that has extra constraints. Conversely, you
+can specify `auto` as the constraints in the selected option.
+
+But on balance, it seemed better to try putting the explicit constraints into
+the implementations so that we have more tools to incrementally roll out changes
+to interface constraints even though those rollouts will as a consequence be
+more noisy in some cases. If experience shows that this is a really bad
+tradeoff, we should revisit it.
+
+#### Inferring associated types from method signatures
+
+The last option considered is used by Swift.
+[Swift allows the value of an associated type to be omitted when it can be determined from the method signatures in the implementation](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID190).
+For the above example, this would mean figuring out `ElementType == T` from
+context:
+
+```
+class DynamicArray(T:! Type) {
+  ...
+  impl as Stack {
+    // Not needed: let ElementType:! Type = T;
+    fn Push[addr me: Self*](value: T);
+    ...
+  }
+}
+```
+
+One benefit is that it allows an interface to evolve by adding an associated
+type, without having to then modify all implementations of that interface.
+
+One concern is this might be a little more complicated in the presence of method
+overloads with [default implementations](interface-defaults), since it might not
+be clear how they should match up, as in this example:
+
+```
+interface Has2OverloadsWithDefaults {
+  let T:! StackAssociatedType;
+  fn F[me: Self](x: DynamicArray(T), y: T) { ... }
+  fn F[me: Self](x: T, y: T.ElementType) { ... }
+}
+
+class S {
+  impl as Has2OverloadsWithDefaults {
+     // Unclear if T == DynamicArray(Int) or
+     // T == DynamicArray(DynamicArray(Int)).
+     fn F[me: Self](
+         x: DynamicArray(DynamicArray(Int)),
+         y: DynamicArray(Int)) { ... }
+  }
+}
+```
+
+Not to say this can't be resolved, but it does add complexity.
+[Swift considered](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#associated-type-inference)
+removing this feature because it was the one thing in Swift that required global
+type inference, which they otherwise avoided. They
+[ultimately decided to keep the feature](https://github.com/apple/swift-evolution/blob/main/proposals/0108-remove-assoctype-inference.md).
+
+This option was only very briefly discussed and not preferred because:
+
+-   It came with complexity of inference.
+-   It seemed unnecessary.
+
+### Value patterns
+
+We considered an alternative to the `type_of` approach from
+[the parameterized interfaces section](/docs/design/generics/details.md#parameterized-interfaces)
+for binding `T` to a type mentioned later in the parameter list. We could
+instead allow functions to have value patterns without a `:`, as in:
+
+```
+fn PeekAtTopOfStackParameterized
+    [T:! Type, StackType:! StackParameterized(T)]
+    (s: StackType*, T) -> T { ... }
+```
+
+However, we don't want to allow value patterns more generally so we can reject
+declarations like `fn F(Int)` when users almost certainly meant `fn F(i: Int)`.
+
+### Deduced interface parameters
+
+The Carbon team considered and then rejected the idea that we would have two
+kinds of interface parameters. "Multi" parameters would work as described in the
+[detailed design document](/docs/design/generics/details.md#parameterized-interfaces).
+"Deducible" type parameters would only allow one implementation of an interface,
+not one per interface & type parameter combination. These deducible type
+parameters could be inferred like
+[associated types](/docs/design/generics/details.md#associated-types) are. For
+example, we could make a `Stack` interface that took a deducible `ElementType`
+parameter. You would only be able to implement that interface once for a type,
+which would allow you to infer the `ElementType` parameter like so:
+
+```
+fn PeekAtTopOfStack[ElementType:! Type, StackType:! Stack(ElementType)]
+    (s: StackType*) -> ElementType { ... }
+```
+
+This can result in more concise code for interfaces where you generally need to
+talk about some parameter anytime you use that interface. For example,
+`NTuple(N, type)` is much shorter without having to specify names with the
+arguments.
+
+#### Rationale for the rejection
+
+-   Having only one type of parameter simplifies the language.
+-   Multi parameters express something we need, while deducible parameters can
+    always be changed to associated types.
+-   One implementation per interface & type parameter combination is more
+    consistent with other parameterized constructs in Carbon. For example,
+    parameterized types `Foo(A)` and `Foo(B)` are distinct, unconnected types.
+-   It would be hard to give clear guidance on when to use associated types
+    versus deducible type parameters, since which is best for a particular use
+    is more of a subtle judgement call.
+-   Deducible parameters in structural interfaces require additional rules to
+    ensure they can be deduced unambiguously.
+
+In addition, deducible interface parameters would complicate the lookup rules
+for impls.
+
+##### Impl lookup rules with deducible interface parameters
+
+Interface implementation is Carbon's only language construct that allows open
+extension, and this sort of open extension is needed to address the "expression
+problem" in programming language design. However, we need to limit which
+libraries can implement an interface for a type so we can be guaranteed to see
+the implementation when we try and use it.
+
+So the question becomes: can we allow an implementation of a parameterized
+interface `I(T)` for a type `A` to be in the same library as `T`, or can it only
+be provided with `I` or `A`? The answer is "yes" if `T` is "multi" and "no" if
+`T` is deducible.
+
+The problem with defining the implementation with a deducible `T` is that it
+would allow users to violate
+[coherence](/docs/design/generics/goals.md#coherence). Consider this collection
+of libraries, where there are implementations for an interface `I(T)` for a type
+`A`, and those implementations are in the libraries defining the type parameter:
+
+```
+package X library "I and A" api;
+
+interface I(Type:$ T) { ... }
+
+struct A { ... }
+```
+
+```
+package Y library "T1" api;
+
+import X library "I and A";
+
+struct T1 { ... }
+
+// Type `X.A` has an implementation for `X.I(T)` for `T == Y.T1`.
+impl X.I(T1) for X.A { ... }
+```
+
+```
+package Z library "T2" api;
+
+import X library "I and A";
+
+struct T2 { ... }
+
+// Type `X.A` has an implementation for `X.I(T)` for `T == Z.T2`.
+impl X.I(T2) for X.A { ... }
+```
+
+```
+package Main api;
+
+import X library "I and A";
+// Consider what happens if we include different combinations
+// of the following two statements:
+// import Y library "T1";
+// import Z library "T2";
+
+// Function `F` is called with value `a` with type `U`,
+// where `U` implements interface `X.I(T)` for some type `T`.
+fn F[Type:$ T, X.I(T):$ U](U:$ a) { ... }
+
+fn Main() {
+  var X.A: a = X.A.Init();
+  F(a);
+}
+```
+
+(In the example, each library is in a different package, but the packages are
+not the important part here.)
+
+The `F(a)` call triggers a lookup for implementations of the interface `X.I(T)`
+for some `T`. There exists such implementations in both libraries `Y.T1` and
+`Z.T2` for different values of `T`. This has a number of sad consequences:
+
+-   "Import what you use" is hard to measure: libraries `Y.T1` and `Z.T2` are
+    important and used even though `Y` and `Z` are not mentioned outside the
+    `import` statement.
+-   The call `F(a)` has different interpretations depending on what libraries
+    are imported:
+    -   If neither is imported, it is an error.
+    -   If both are imported, it is ambiguous.
+    -   If only one is imported, you get totally different code executed
+        depending on which it is.
+-   We have no way of enforcing a "one implementation per interface" rule that
+    would prevent the call to `F` from being ambiguous.
+
+Basically, there is nothing guaranteeing that we import libraries defining the
+types that are used as interface parameters if we allow the interface parameters
+to be deduced.
+
+### Only associated types, no interface parameters
+
+This is what Swift does, but doesn't allow us to use interfaces to express
+operator overloads. For example, a vector should be able to be added to either a
+vector or a point. So we follow Rust, which has trait parameters in addition to
+associated types and uses them to define the behavior of operators.
+
+### Others
+
+Other alternatives considered will be in a future proposal. Some of them can be
+seen in a rough form in
+[#36](https://github.com/carbon-language/carbon-lang/pull/36).