Selaa lähdekoodia

Update and edit pass through generic design docs (#965)

Includes a variety of changes:

Int -> i32
this -> me
expand terminology doc and add links to it
fix text to reflect inline external impl introduced in Support external impl in class and adapter scopes. #905
no longer have plans for runtime type parameters
style updates like removing parentheticals and "we"
observe is a "declaration" not a "statement", since it can appear outside function bodies
many individual updates, clean-ups, and fixes

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
josh11b 4 vuotta sitten
vanhempi
sitoutus
3f759ed58c
3 muutettua tiedostoa jossa 342 lisäystä ja 245 poistoa
  1. 244 197
      docs/design/generics/details.md
  2. 38 33
      docs/design/generics/overview.md
  3. 60 15
      docs/design/generics/terminology.md

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 244 - 197
docs/design/generics/details.md


+ 38 - 33
docs/design/generics/overview.md

@@ -80,8 +80,9 @@ Summary of how Carbon generics work:
 -   A function with a generic type parameter can have the same function body as
     an unparameterized one. Functions can freely mix generic, template, and
     regular parameters.
--   Interfaces can require other interfaces be implemented, or
-    [extend](terminology.md#extending-an-interface) them.
+-   Interfaces can require other interfaces be implemented.
+-   Interfaces can [extend](terminology.md#extending-an-interface) required
+    interfaces.
 -   The `&` operation on type-of-types allows you conveniently combine
     interfaces. It gives you all the names that don't conflict.
 -   You may also declare a new type-of-type directly using
@@ -98,7 +99,7 @@ instead of making near-duplicates for very similar situations, much like C++
 templates. For example, instead of having one function per type-you-can-sort:
 
 ```
-fn SortInt32Vector(a: Vector(Int32)*) { ... }
+fn SortInt32Vector(a: Vector(i32)*) { ... }
 fn SortStringVector(a: Vector(String)*) { ... }
 ...
 ```
@@ -110,9 +111,10 @@ elements:
 fn SortVector(T:! Comparable, a: Vector(T)*) { ... }
 ```
 
-The syntax above adds a `!` to indicate that the parameter named `T` is generic.
+The syntax above adds a `!` to indicate that the parameter named `T` is generic
+and the caller will have to provide a value known at compile time.
 
-Given an `Int32` vector `iv`, `SortVector(Int32, &iv)` is equivalent to
+Given an `i32` vector `iv`, `SortVector(i32, &iv)` is equivalent to
 `SortInt32Vector(&iv)`. Similarly for a `String` vector `sv`,
 `SortVector(String, &sv)` is equivalent to `SortStringVector(&sv)`. Thus, we can
 sort any vector containing comparable elements using this single `SortVector`
@@ -154,7 +156,7 @@ Example:
 ```
 interface Comparable {
   // `Less` is an associated method.
-  fn Less[me: Self](that: Self) -> Bool;
+  fn Less[me: Self](rhs: Self) -> Bool;
 }
 ```
 
@@ -168,8 +170,7 @@ do some checking given a function definition, but more checking of the
 definition is required after seeing the call sites once all the
 [instantiations](terminology.md#instantiation) are known.
 
-Note: The doc on [Generics terminology](terminology.md) goes into more detail
-about the
+Note: [Generics terminology](terminology.md) goes into more detail about the
 [differences between generics and templates](terminology.md#generic-versus-template-parameters).
 
 ### Implementing interfaces
@@ -208,19 +209,23 @@ class Song {
 // the library defining `Song` or `Comparable`.
 external impl Song as Comparable {
   // Could use either `Self` or `Song` here.
-  fn Less[me: Self](that: Self) -> Bool { ... }
+  fn Less[me: Self](rhs: Self) -> Bool { ... }
 }
 ```
 
-Implementations may be defined within the class definition itself or externally.
-External implementations may be defined in the library defining the interface.
+Implementations may be defined within the class definition itself or
+out-of-line. Implementations may optionally be start with the `external` keyword
+to say the members of the interface are not unqualified members of the class.
+Out-of-line implementations must be external. External implementations may be
+defined in the library defining either the class or the interface.
 
 #### Qualified and unqualified access
 
-The methods of an interface implemented within the class definition may be
-called with the unqualified syntax. All methods of implemented interfaces may be
-called with the qualified syntax, whether they are defined internally or
-externally.
+The methods of an interface implemented internally within the class definition
+may be called with the ordinary unqualified member syntax. Methods of all
+implemented interfaces may be called with the
+[qualified member syntax](terminology.md#qualified-and-unqualified-member-names),
+whether they are defined internally or externally.
 
 ```
 var song: Song;
@@ -242,7 +247,7 @@ specific type value assigned to `T` is not known when type checking the
 `SortVector` function. Instead it is the constraints on `T` that let the
 compiler know what operations may be performed on values of type `T`. Those
 constraints are represented by the type of `T`, a
-[**_type-of-type_**](terminology.md#type-constraints).
+[**_type-of-type_**](terminology.md#type-of-type).
 
 In general, a type-of-type describes the capabilities of a type, while a type
 defines specific implementations of those capabilities.
@@ -281,7 +286,7 @@ SortVectorDeduced(&anIntVector);
 SortVectorDeduced(&aStringVector);
 ```
 
-and the compiler deduces that the `T` argument should be set to `Int32` or
+and the compiler deduces that the `T` argument should be set to `i32` or
 `String` from the type of the argument.
 
 Deduced arguments are always determined from the call and its explicit
@@ -328,7 +333,7 @@ Interfaces can require other interfaces be implemented:
 
 ```
 interface Equatable {
-  fn IsEqual[me: Self](that: Self) -> Bool;
+  fn IsEqual[me: Self](rhs: Self) -> Bool;
 }
 
 // `Iterable` requires that `Equatable` is implemented.
@@ -339,33 +344,33 @@ interface Iterable {
 ```
 
 The `extends` keyword is used to [extend](terminology.md#extending-an-interface)
-another interface. If interface `Child` extends interface `Parent`, `Parent`'s
-interface is both required and all its methods are included in `Child`'s
+another interface. If interface `Derived` extends interface `Base`, `Base`'s
+interface is both required and all its methods are included in `Derived`'s
 interface.
 
 ```
 // `Hashable` extends `Equatable`.
 interface Hashable {
   extends Equatable;
-  fn Hash[me: Self]() -> UInt64;
+  fn Hash[me: Self]() -> u64;
 }
 // `Hashable` is equivalent to:
 interface Hashable {
   impl as Equatable;
   alias IsEqual = Equatable.IsEqual;
-  fn Hash[me: Self]() -> UInt64;
+  fn Hash[me: Self]() -> u64;
 }
 ```
 
-A type may implement the parent interface implicitly by implementing all the
-methods in the child implementation.
+A type may implement the base interface implicitly by implementing all the
+methods in the implementation of the derived interface.
 
 ```
 class Key {
   // ...
   impl as Hashable {
-    fn IsEqual[me: Key](that: Key) -> Bool { ... }
-    fn Hash[me: Key]() -> UInt64 { ... }
+    fn IsEqual[me: Key](rhs: Key) -> Bool { ... }
+    fn Hash[me: Key]() -> u64 { ... }
   }
   // No need to separately implement `Equatable`.
 }
@@ -381,17 +386,17 @@ It gives you all the names that don't conflict.
 
 ```
 interface Renderable {
-  fn GetCenter[me: Self]() -> (Int, Int);
+  fn GetCenter[me: Self]() -> (i32, i32);
   // Draw the object to the screen
   fn Draw[me: Self]();
 }
 interface EndOfGame {
-  fn SetWinner[addr me: Self*](player: Int);
+  fn SetWinner[addr me: Self*](player: i32);
   // Indicate the game was a draw
   fn Draw[addr me: Self*]();
 }
 
-fn F[T:! Renderable & EndOfGame](game_state: T*) -> (Int, Int) {
+fn F[T:! Renderable & EndOfGame](game_state: T*) -> (i32, i32) {
   game_state->SetWinner(1);
   return game_state->Center();
 }
@@ -472,10 +477,10 @@ At that point, two erasures occur:
 
 ### 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.
+Carbon has a mechanism called [adapting types](terminology.md#adapting-a-type))
+to create new types that are [compatible](terminology.md#compatible-types) 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.
 

+ 60 - 15
docs/design/generics/terminology.md

@@ -17,6 +17,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
         -   [Compile-time duck typing](#compile-time-duck-typing)
         -   [Ad-hoc polymorphism](#ad-hoc-polymorphism)
     -   [Constrained genericity](#constrained-genericity)
+    -   [Dependent names](#dependent-names)
     -   [Definition checking](#definition-checking)
         -   [Complete definition checking](#complete-definition-checking)
         -   [Early versus late type checking](#early-versus-late-type-checking)
@@ -27,6 +28,9 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     -   [Named constraints](#named-constraints)
 -   [Associated entity](#associated-entity)
 -   [Impls: Implementations of interfaces](#impls-implementations-of-interfaces)
+    -   [Internal impl](#internal-impl)
+    -   [External impl](#external-impl)
+-   [Qualified and unqualified member names](#qualified-and-unqualified-member-names)
 -   [Compatible types](#compatible-types)
 -   [Subtyping and casting](#subtyping-and-casting)
 -   [Adapting a type](#adapting-a-type)
@@ -191,10 +195,12 @@ fn F(x: Int) -> Bool;
 A generic function `G` can call `F` with a type like `T*` that can not possibly
 call the `F(Int)` overload for `F`, and so it can consistently determine the
 return type of `F`. But `G` can't call `F` with an argument that could match
-either overload. (It is undecided what to do in the situation where `F` is
-overloaded, but the signatures are consistent and so callers could still
-typecheck calls to `F`. This still poses problems for the dynamic strategy for
-compiling generics.)
+either overload.
+
+**Note:** It is undecided what to do in the situation where `F` is overloaded,
+but the signatures are consistent and so callers could still typecheck calls to
+`F`. This still poses problems for the dynamic strategy for compiling generics,
+in a similar way to impl specialization.
 
 ### Constrained genericity
 
@@ -206,9 +212,10 @@ those operations that are guaranteed by the constraints.
 
 With templates using unconstrained genericity, you may perform any operation in
 the body of the function, and they will be checked against the specific types
-used in calls. You can still have constraints, but they are optional. They will
-only be used to resolve overloaded calls to the template and provide clearer
-error messages.
+used in calls. You can still have constraints, but they are optional in that
+they could be removed and the function would still have the same capabilities.
+Constraints only affect the caller, which will use them to resolve overloaded
+calls to the template and provide clearer error messages.
 
 With generics using constrained genericity, the function body can be checked
 against the signature at the time of definition. Note that it is still perfectly
@@ -216,16 +223,24 @@ permissible to have no constraints on a type; that just means that you can only
 perform operations that work for all types (such as manipulate pointers to
 values of that type) in the body of the function.
 
+### Dependent names
+
+A name is said to be _dependent_ if it depends on some generic or template
+parameter. Note: this matches
+[the use of the term "dependent" in C++](https://www.google.com/search?q=c%2B%2B+dependent+name),
+not as in [dependent types](https://en.wikipedia.org/wiki/Dependent_type).
+
 ### Definition checking
 
 Definition checking is the process of semantically checking the definition of
 parameterized code for correctness _independently_ of any particular arguments.
 It includes type checking and other semantic checks. It is possible, even with
-templates, to check semantics of expressions that are not dependent on any
-template parameter in the definition. Adding constraints to template parameters
-and/or switching them to be generic allows the compiler to increase how much of
-the definition can be checked. Any remaining checks are delayed until
-[instantiation](#instantiation), which can fail.
+templates, to check semantics of expressions that are not
+[dependent](#dependent-names) on any template parameter in the definition.
+Adding constraints to template parameters and/or switching them to be generic
+allows the compiler to increase how much of the definition can be checked. Any
+remaining checks are delayed until [instantiation](#instantiation), which can
+fail.
 
 #### Complete definition checking
 
@@ -244,7 +259,8 @@ This occurs for regular and generic values.
 
 Late type checking is where expressions and statements may only be fully
 typechecked once calling information is known. Late type checking delays
-complete definition checking. This occurs for template dependent values.
+complete definition checking. This occurs for
+[template-dependent](#dependent-names) values.
 
 ## Deduced parameter
 
@@ -291,7 +307,7 @@ A "nominal" interface is one where we say a type can only satisfy an interface
 if there is some explicit statement saying so, for example by defining an
 [impl](#impls-implementations-of-interfaces). This allows "satisfies the
 interface" to have additional semantic meaning beyond what is directly checkable
-by the compiler. For example, knowing whether the "Draw" function means "render
+by the compiler. For example, knowing whether the `Draw` function means "render
 an image to the screen" or "take a card from the top of a deck of cards"; or
 that a `+` operator is commutative (and not, say, string concatenation).
 
@@ -305,7 +321,9 @@ Named constraints are "structural" in the sense that they match a type based on
 meeting some criteria rather than an explicit statement in the type's
 definition. The criteria for a named constraint, however, are less focused on
 the type's API and instead might include a set of nominal interfaces that the
-type must implement.
+type must implement and constraints on the
+[associated entities](#associated-entity) and
+[interface type parameters](#interface-type-parameters-and-associated-types).
 
 ## Associated entity
 
@@ -334,6 +352,33 @@ are given. Impls are needed for [nominal interfaces](#nominal-interfaces);
 by requiring an impl to be defined. In can still make sense to implement a named
 constraint as a way to implement all of the interfaces it requires.
 
+### Internal impl
+
+A type that implements an interface _internally_ has all the named members of
+the interface as named members of the type. This means that the members of the
+interface may be accessed as either
+[unqualified or qualified members](#qualified-and-unqualified-member-names).
+
+### External impl
+
+In contrast, a type that implements an interface _externally_ does not include
+the named members of the interface in the type. The members of the interface are
+still implemented by the type, though, and so may be accessed using the
+[qualified names](#qualified-and-unqualified-member-names) of those members.
+
+## Qualified and unqualified member names
+
+A qualified member includes both the name of the interface defining the member
+and the name of the member. So if `String` implements `Comparable` which has a
+`Less` method, and `s1` and `s2` are variables of type `String`, then the `Less`
+method may be called using the qualified member name by writing
+`s1.(Comparable.Less)(s2)`.
+
+If the interface is implemented internally, then the method can be called using
+the unqualified syntax as well. If `String` implements `Printable` internally,
+then `s1.Print()` calls the `Print` method of `Printable` as an unqualified
+member.
+
 ## Compatible types
 
 Two types are compatible if they have the same notional set of values and

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä