|
@@ -21,17 +21,17 @@ pointers to other design documents that dive deeper into individual topics.
|
|
|
- [Contrast with templates](#contrast-with-templates)
|
|
- [Contrast with templates](#contrast-with-templates)
|
|
|
- [Implementing interfaces](#implementing-interfaces)
|
|
- [Implementing interfaces](#implementing-interfaces)
|
|
|
- [Accessing members of interfaces](#accessing-members-of-interfaces)
|
|
- [Accessing members of interfaces](#accessing-members-of-interfaces)
|
|
|
- - [Type-of-types](#type-of-types)
|
|
|
|
|
|
|
+ - [Facet types](#facet-types)
|
|
|
- [Generic functions](#generic-functions)
|
|
- [Generic functions](#generic-functions)
|
|
|
- [Deduced parameters](#deduced-parameters)
|
|
- [Deduced parameters](#deduced-parameters)
|
|
|
- - [Generic type parameters](#generic-type-parameters)
|
|
|
|
|
|
|
+ - [Facet parameters](#facet-parameters)
|
|
|
- [Requiring or extending another interface](#requiring-or-extending-another-interface)
|
|
- [Requiring or extending another interface](#requiring-or-extending-another-interface)
|
|
|
- [Combining interfaces](#combining-interfaces)
|
|
- [Combining interfaces](#combining-interfaces)
|
|
|
- [Named constraints](#named-constraints)
|
|
- [Named constraints](#named-constraints)
|
|
|
- [Type erasure](#type-erasure)
|
|
- [Type erasure](#type-erasure)
|
|
|
- [Adapting types](#adapting-types)
|
|
- [Adapting types](#adapting-types)
|
|
|
- - [Interface input and output types](#interface-input-and-output-types)
|
|
|
|
|
- - [Associated types](#associated-types)
|
|
|
|
|
|
|
+ - [Interface inputs and outputs](#interface-inputs-and-outputs)
|
|
|
|
|
+ - [Associated constants](#associated-constants)
|
|
|
- [Parameterized interfaces](#parameterized-interfaces)
|
|
- [Parameterized interfaces](#parameterized-interfaces)
|
|
|
- [Constraints](#constraints)
|
|
- [Constraints](#constraints)
|
|
|
- [Parameterized impl declarations](#parameterized-impl-declarations)
|
|
- [Parameterized impl declarations](#parameterized-impl-declarations)
|
|
@@ -43,8 +43,20 @@ pointers to other design documents that dive deeper into individual topics.
|
|
|
|
|
|
|
|
## Goals
|
|
## Goals
|
|
|
|
|
|
|
|
-The goal of Carbon generics is to provide an alternative to Carbon (or C++)
|
|
|
|
|
-templates. Generics in this form should provide many advantages, including:
|
|
|
|
|
|
|
+Carbon [generics](terminology.md#generic-means-compile-time-parameterized)
|
|
|
|
|
+supports generalizing code to apply to more situations by adding compile-time
|
|
|
|
|
+parameters, allowing
|
|
|
|
|
+[generic programming](https://en.wikipedia.org/wiki/Generic_programming). Carbon
|
|
|
|
|
+supports both
|
|
|
|
|
+[checked and template](terminology.md#checked-versus-template-parameters)
|
|
|
|
|
+generics.
|
|
|
|
|
+
|
|
|
|
|
+Template generics provide a similar model to C++ templates, to help with interop
|
|
|
|
|
+and migration. They can be more convenient to write, and support some use cases,
|
|
|
|
|
+like [metaprogramming](https://en.wikipedia.org/wiki/Metaprogramming), that are
|
|
|
|
|
+difficult with checked generics.
|
|
|
|
|
+
|
|
|
|
|
+Checked generics are an alternative that has advantages including:
|
|
|
|
|
|
|
|
- Function calls and bodies are checked independently against the function
|
|
- Function calls and bodies are checked independently against the function
|
|
|
signatures.
|
|
signatures.
|
|
@@ -52,6 +64,9 @@ templates. Generics in this form should provide many advantages, including:
|
|
|
- Fast builds, particularly development builds.
|
|
- Fast builds, particularly development builds.
|
|
|
- Support for both static and dynamic dispatch.
|
|
- Support for both static and dynamic dispatch.
|
|
|
|
|
|
|
|
|
|
+Checked generics do have some restrictions, but are expected to be more
|
|
|
|
|
+appropriate at public API boundaries than templates.
|
|
|
|
|
+
|
|
|
For more detail, see [the detailed discussion of generics goals](goals.md) and
|
|
For more detail, see [the detailed discussion of generics goals](goals.md) and
|
|
|
[generics terminology](terminology.md).
|
|
[generics terminology](terminology.md).
|
|
|
|
|
|
|
@@ -59,35 +74,47 @@ For more detail, see [the detailed discussion of generics goals](goals.md) and
|
|
|
|
|
|
|
|
Summary of how Carbon generics work:
|
|
Summary of how Carbon generics work:
|
|
|
|
|
|
|
|
-- _Generics_ are parameterized functions and types that can apply generally.
|
|
|
|
|
- They are used to avoid writing specialized, near-duplicate code for similar
|
|
|
|
|
- situations.
|
|
|
|
|
-- Generics are written using _interfaces_ which have a name and describe
|
|
|
|
|
- methods, functions, and other entities for types to implement.
|
|
|
|
|
|
|
+- _Generics_ are compile-time parameterized functions, types, and other
|
|
|
|
|
+ language constructs. Those parameters allow a single definition to apply
|
|
|
|
|
+ more generally. They are used to avoid writing specialized, near-duplicate
|
|
|
|
|
+ code for similar situations.
|
|
|
|
|
+- The definition of a _checked_ generic is typechecked once, without having to
|
|
|
|
|
+ know the specific argument values of the generic parameters it is
|
|
|
|
|
+ instantiated with. Typechecking the definition of a checked generic requires
|
|
|
|
|
+ a precise contract specifying the requirements on the argument values.
|
|
|
|
|
+- For parameters that will be used as types, those requirements are written
|
|
|
|
|
+ using _interfaces_. Interfaces have a name and describe 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.
|
|
|
|
|
-- Implementations may be part of the type's definition, in which case you can
|
|
|
|
|
- directly call the interface's methods on those types. Or, they may be
|
|
|
|
|
- external, in which case the implementation is allowed to be defined in the
|
|
|
|
|
- library defining the interface.
|
|
|
|
|
-- Interfaces are used as the type of a generic type parameter, acting as a
|
|
|
|
|
- _type-of-type_. Type-of-types in general specify the capabilities and
|
|
|
|
|
- requirements of the type. Types define specific implementations of those
|
|
|
|
|
- capabilities. Inside such a generic function, the API of the type is
|
|
|
|
|
- [erased](terminology.md#type-erasure), except for the names defined in the
|
|
|
|
|
- type-of-type.
|
|
|
|
|
-- _Deduced parameters_ are parameters whose values are determined by the
|
|
|
|
|
- values and (most commonly) the types of the explicit arguments. Generic type
|
|
|
|
|
- parameters are typically deduced.
|
|
|
|
|
-- 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.
|
|
|
|
|
|
|
+ their functionality. A given type may implement an interface at most once.
|
|
|
|
|
+- Implementations may be declared inline in the body of a class definition or
|
|
|
|
|
+ out-of-line.
|
|
|
|
|
+- Types may _extend_ an implementation declared inline, in which case you can
|
|
|
|
|
+ directly call the interface's methods on those types.
|
|
|
|
|
+- Out-of-line implementations may be defined in the library defining the
|
|
|
|
|
+ interface as an alternative to the type, or
|
|
|
|
|
+ [the library defining a type argument](#parameterized-impl-declarations).
|
|
|
|
|
+- Interfaces may be used as the type of a generic parameter. Interfaces are
|
|
|
|
|
+ _facet types_, whose values are the subset of all types that implement the
|
|
|
|
|
+ interface. Facet types in general specify the capabilities and requirements
|
|
|
|
|
+ of the type. The value of a interface is called a _facet_. Facets are not
|
|
|
|
|
+ types, but are usable as types.
|
|
|
|
|
+- With a template generic, the concrete argument value used by the caller is
|
|
|
|
|
+ used for name lookup and typechecking. With checked generics, that is all
|
|
|
|
|
+ done with the declared restrictions expressed as the types of bindings in
|
|
|
|
|
+ the declaration. Inside the body of a checked generic with a facet
|
|
|
|
|
+ parameter, the API of the facet is just the names defined by the facet type.
|
|
|
|
|
+- _Deduced parameters_ are parameters whose values are determined by the types
|
|
|
|
|
+ of the explicit arguments. Generic facet parameters are typically deduced.
|
|
|
|
|
+- A function with a generic parameter can have the same function body as an
|
|
|
|
|
+ unparameterized one. Functions can freely mix checked, template, and regular
|
|
|
|
|
+ parameters.
|
|
|
- Interfaces can require other interfaces be implemented.
|
|
- Interfaces can require other interfaces be implemented.
|
|
|
- Interfaces can [extend](terminology.md#extending-an-interface) required
|
|
- Interfaces can [extend](terminology.md#extending-an-interface) required
|
|
|
interfaces.
|
|
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
|
|
|
|
|
|
|
+- The `&` operation on facet types allows you conveniently combine interfaces.
|
|
|
|
|
+ It gives you all the names that don't conflict.
|
|
|
|
|
+- You may also declare a new facet type directly using
|
|
|
["named constraints"](terminology.md#named-constraints). Named constraints
|
|
["named constraints"](terminology.md#named-constraints). Named constraints
|
|
|
can express requirements that multiple interfaces be implemented, and give
|
|
can express requirements that multiple interfaces be implemented, and give
|
|
|
you control over how name conflicts are handled.
|
|
you control over how name conflicts are handled.
|
|
@@ -114,8 +141,9 @@ elements:
|
|
|
fn SortVector(T:! Comparable, a: Vector(T)*) { ... }
|
|
fn SortVector(T:! Comparable, a: Vector(T)*) { ... }
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-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.
|
|
|
|
|
|
|
+The syntax above adds a `!` to indicate that the parameter named `T` is
|
|
|
|
|
+compile-time. By default compile-time parameters are _checked_, the `template`
|
|
|
|
|
+keyword may be added to make it a _template generic_.
|
|
|
|
|
|
|
|
Given an `i32` vector `iv`, `SortVector(i32, &iv)` is equivalent to
|
|
Given an `i32` vector `iv`, `SortVector(i32, &iv)` is equivalent to
|
|
|
`SortInt32Vector(&iv)`. Similarly for a `String` vector `sv`,
|
|
`SortInt32Vector(&iv)`. Similarly for a `String` vector `sv`,
|
|
@@ -128,14 +156,16 @@ This ability to generalize makes `SortVector` a _generic_.
|
|
|
### Interfaces
|
|
### Interfaces
|
|
|
|
|
|
|
|
The `SortVector` function requires a definition of `Comparable`, with the goal
|
|
The `SortVector` function requires a definition of `Comparable`, with the goal
|
|
|
-that the compiler can:
|
|
|
|
|
|
|
+that the compiler can perform checking. This has two pieces:
|
|
|
|
|
|
|
|
-- completely type check a generic definition without information from where
|
|
|
|
|
- it's called.
|
|
|
|
|
-- completely type check a call to a generic with information only from the
|
|
|
|
|
- function's signature, and not from its body.
|
|
|
|
|
|
|
+- definition checking: completely type check a checked-generic definition
|
|
|
|
|
+ without information from calls;
|
|
|
|
|
+- encapsulation: completely type check a call to a generic with information
|
|
|
|
|
+ only from the function's signature, and not from its body. For Rust, this is
|
|
|
|
|
+ called
|
|
|
|
|
+ "[Rust's Golden Rule](https://steveklabnik.com/writing/rusts-golden-rule)."
|
|
|
|
|
|
|
|
-In this example, then, `Comparable` is an _interface_.
|
|
|
|
|
|
|
+In this example, `Comparable` is an _interface_.
|
|
|
|
|
|
|
|
Interfaces describe all the requirements needed for the type `T`. Given that the
|
|
Interfaces describe all the requirements needed for the type `T`. Given that the
|
|
|
compiler knows `T` satisfies those requirements, it can type check the body of
|
|
compiler knows `T` satisfies those requirements, it can type check the body of
|
|
@@ -174,13 +204,13 @@ an interface.
|
|
|
|
|
|
|
|
#### Contrast with templates
|
|
#### Contrast with templates
|
|
|
|
|
|
|
|
-Contrast these generics with a C++ template, where the compiler may be able to
|
|
|
|
|
-do some checking given a function definition, but more checking of the
|
|
|
|
|
-definition is required after seeing the call sites once all the
|
|
|
|
|
|
|
+Contrast these checked generics with a Carbon or C++ template, where the
|
|
|
|
|
+compiler may be able to 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.
|
|
[instantiations](terminology.md#instantiation) are known.
|
|
|
|
|
|
|
|
Note: [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#checked-versus-template-parameters).
|
|
|
|
|
|
|
+[differences between checked and template generic parameters](terminology.md#checked-versus-template-parameters).
|
|
|
|
|
|
|
|
### Implementing interfaces
|
|
### Implementing interfaces
|
|
|
|
|
|
|
@@ -225,24 +255,24 @@ impl Song as Comparable {
|
|
|
Implementations may be defined within the class definition itself or
|
|
Implementations may be defined within the class definition itself or
|
|
|
out-of-line. Implementations may optionally start with the `extend` keyword to
|
|
out-of-line. Implementations may optionally start with the `extend` keyword to
|
|
|
say the members of the interface are also members of the class, which may only
|
|
say the members of the interface are also members of the class, which may only
|
|
|
-be used in a class scope. Otherwise, implementations may be defined in the
|
|
|
|
|
-library defining either the class or the interface.
|
|
|
|
|
|
|
+be used in a class scope. Out-of-line implementations may be defined in the
|
|
|
|
|
+library defining the class, the interface, or
|
|
|
|
|
+[a type argument](#parameterized-impl-declarations).
|
|
|
|
|
|
|
|
#### Accessing members of interfaces
|
|
#### Accessing members of interfaces
|
|
|
|
|
|
|
|
-The methods of an interface implemented internally within the class definition
|
|
|
|
|
-may be called with the
|
|
|
|
|
|
|
+Methods from an interface that a class extends may be called with the
|
|
|
[simple member access syntax](terminology.md#simple-member-access). Methods of
|
|
[simple member access syntax](terminology.md#simple-member-access). Methods of
|
|
|
all implemented interfaces may be called with a
|
|
all implemented interfaces may be called with a
|
|
|
[qualified member access expression](terminology.md#qualified-member-access-expression),
|
|
[qualified member access expression](terminology.md#qualified-member-access-expression),
|
|
|
-whether they are defined internally or externally.
|
|
|
|
|
|
|
+whether the class extends them or not.
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
var song: Song;
|
|
var song: Song;
|
|
|
// `song.Print()` is allowed, unlike `song.Play()`.
|
|
// `song.Print()` is allowed, unlike `song.Play()`.
|
|
|
song.Print();
|
|
song.Print();
|
|
|
-// `Less` is defined in `Comparable`, which is
|
|
|
|
|
-// implemented externally for `Song`
|
|
|
|
|
|
|
+// `Less` is defined in `Comparable`, which `Song`
|
|
|
|
|
+// does not extend the implementation of.
|
|
|
song.(Comparable.Less)(song);
|
|
song.(Comparable.Less)(song);
|
|
|
// Can also call `Print` using a qualified member
|
|
// Can also call `Print` using a qualified member
|
|
|
// access expression, using the compound member access
|
|
// access expression, using the compound member access
|
|
@@ -250,28 +280,28 @@ song.(Comparable.Less)(song);
|
|
|
song.(Printable.Print)();
|
|
song.(Printable.Print)();
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### Type-of-types
|
|
|
|
|
|
|
+### Facet types
|
|
|
|
|
|
|
|
To type check a function, the compiler needs to be able to verify that uses of a
|
|
To type check a function, the compiler needs to be able to verify that uses of a
|
|
|
value match the capabilities of the value's type. In `SortVector`, the parameter
|
|
value match the capabilities of the value's type. In `SortVector`, the parameter
|
|
|
-`T` is a type, but that type is a generic parameter. That means that the
|
|
|
|
|
-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#facet-type).
|
|
|
|
|
-
|
|
|
|
|
-In general, a type-of-type describes the capabilities of a type, while a type
|
|
|
|
|
|
|
+`T` is a type, but that type is a checked-generic, or _symbolic_, parameter.
|
|
|
|
|
+That means that the 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
|
|
|
|
|
+[**_facet type_**](terminology.md#facet-type).
|
|
|
|
|
+
|
|
|
|
|
+In general, a facet type describes the capabilities of a type, while a type
|
|
|
defines specific implementations of those capabilities. An interface, like
|
|
defines specific implementations of those capabilities. An interface, like
|
|
|
-`Comparable`, may be used as a type-of-type. In that case, the constraint on the
|
|
|
|
|
|
|
+`Comparable`, may be used as a facet type. In that case, the constraint on the
|
|
|
type is that it must implement the interface `Comparable`.
|
|
type is that it must implement the interface `Comparable`.
|
|
|
|
|
|
|
|
-A type-of-type also defines a set of names and a mapping to corresponding
|
|
|
|
|
|
|
+A facet type also defines a set of names and a mapping to corresponding
|
|
|
qualified names. Those names are used for
|
|
qualified names. Those names are used for
|
|
|
[simple member lookup](terminology.md#simple-member-access) in scopes where the
|
|
[simple member lookup](terminology.md#simple-member-access) in scopes where the
|
|
|
value of the type is not known, such as when the type is a generic parameter.
|
|
value of the type is not known, such as when the type is a generic parameter.
|
|
|
|
|
|
|
|
-You may combine interfaces into new type-of-types using
|
|
|
|
|
|
|
+You may combine interfaces into new facet types using
|
|
|
[the `&` operator](#combining-interfaces) or
|
|
[the `&` operator](#combining-interfaces) or
|
|
|
[named constraints](#named-constraints).
|
|
[named constraints](#named-constraints).
|
|
|
|
|
|
|
@@ -314,9 +344,9 @@ call site.
|
|
|
fn Illegal[T:! type, U:! type](x: T) -> U { ... }
|
|
fn Illegal[T:! type, U:! type](x: T) -> U { ... }
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-#### Generic type parameters
|
|
|
|
|
|
|
+#### Facet parameters
|
|
|
|
|
|
|
|
-A function with a generic type parameter can have the same function body as an
|
|
|
|
|
|
|
+A function with a facet parameter can have the same function body as an
|
|
|
unparameterized one.
|
|
unparameterized one.
|
|
|
|
|
|
|
|
```
|
|
```
|
|
@@ -329,20 +359,24 @@ fn PrintIt(p: Song*) {
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-Inside the function body, you can treat the generic type parameter just like any
|
|
|
|
|
-other type. There is no need to refer to or access generic parameters
|
|
|
|
|
-differently because they are defined as generic, as long as you only refer to
|
|
|
|
|
-the names defined by [type-of-type](#type-of-types) for the type parameter.
|
|
|
|
|
|
|
+Inside the function body, you can treat the facet parameter just like any other
|
|
|
|
|
+type. There is no need to refer to or access generic parameters differently
|
|
|
|
|
+because they are defined as generic, as long as you only refer to the names
|
|
|
|
|
+defined by [facet type](#facet-types) for the facet parameter.
|
|
|
|
|
|
|
|
-You may also refer to any of the methods of interfaces required by the
|
|
|
|
|
-type-of-type using a
|
|
|
|
|
-[qualified member access expression](#accessing-members-of-interfaces), as shown
|
|
|
|
|
-in the following sections.
|
|
|
|
|
|
|
+You may also refer to any of the methods of interfaces required by the facet
|
|
|
|
|
+type using a
|
|
|
|
|
+[qualified member access expression](#accessing-members-of-interfaces).
|
|
|
|
|
|
|
|
-A function can have a mix of generic, template, and regular parameters.
|
|
|
|
|
-Likewise, it's allowed to pass a template or generic value to a generic or
|
|
|
|
|
-regular parameter. _However, passing a generic value to a template parameter is
|
|
|
|
|
-future work._
|
|
|
|
|
|
|
+A function can have a mix of checked, template, and regular parameters. Each
|
|
|
|
|
+kind of parameter is defined using a different syntax: a checked parameter is
|
|
|
|
|
+uses a symbolic binding pattern, a template parameter uses a template binding
|
|
|
|
|
+pattern, and a regular parameter uses a runtime binding pattern. Likewise, it's
|
|
|
|
|
+allowed to pass a symbolic or template constant value to a checked or regular
|
|
|
|
|
+parameter. _We have decided to support passing a symbolic value to a template
|
|
|
|
|
+parameter, see
|
|
|
|
|
+[leads issue #2153: Checked generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153),
|
|
|
|
|
+but incorporating it into the design is future work._
|
|
|
|
|
|
|
|
### Requiring or extending another interface
|
|
### Requiring or extending another interface
|
|
|
|
|
|
|
@@ -398,8 +432,8 @@ k.IsEqual(k);
|
|
|
|
|
|
|
|
### Combining interfaces
|
|
### Combining interfaces
|
|
|
|
|
|
|
|
-The `&` operation on type-of-types allows you conveniently combine interfaces.
|
|
|
|
|
-It gives you all the names that don't conflict.
|
|
|
|
|
|
|
+The `&` operation on facet types allows you conveniently combine interfaces. It
|
|
|
|
|
+gives you all the names that don't conflict.
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
interface Renderable {
|
|
interface Renderable {
|
|
@@ -431,7 +465,7 @@ fn BothDraws[T:! Renderable & EndOfGame](game_state: T*) {
|
|
|
|
|
|
|
|
#### Named constraints
|
|
#### Named constraints
|
|
|
|
|
|
|
|
-You may also declare a new type-of-type directly using
|
|
|
|
|
|
|
+You may also declare a new facet type directly using
|
|
|
["named constraints"](terminology.md#named-constraints). Named constraints can
|
|
["named constraints"](terminology.md#named-constraints). Named constraints can
|
|
|
express requirements that multiple interfaces be implemented, and give you
|
|
express requirements that multiple interfaces be implemented, and give you
|
|
|
control over how name conflicts are handled. Named constraints have other
|
|
control over how name conflicts are handled. Named constraints have other
|
|
@@ -461,12 +495,11 @@ fn CallItAll[T:! Combined](game_state: T*, int winner) {
|
|
|
|
|
|
|
|
#### Type erasure
|
|
#### Type erasure
|
|
|
|
|
|
|
|
-Inside a generic function, the API of a type argument is
|
|
|
|
|
-[erased](terminology.md#type-erasure) except for the names defined in the
|
|
|
|
|
-type-of-type. An equivalent model is to say an
|
|
|
|
|
-[archetype](terminology.md#archetype) is used for type checking and name lookup
|
|
|
|
|
-when the actual type is not known in that scope. The archetype has members
|
|
|
|
|
-dictated by the type-of-type.
|
|
|
|
|
|
|
+Inside a generic function, the API of a facet argument is
|
|
|
|
|
+[erased](terminology.md#type-erasure) except for the names defined in the facet
|
|
|
|
|
+type. An equivalent model is to say an [archetype](terminology.md#archetype) is
|
|
|
|
|
+used for type checking and name lookup when the actual type is not known in that
|
|
|
|
|
+scope. The archetype has members dictated by the facet type.
|
|
|
|
|
|
|
|
For example: If there were a class `CDCover` defined this way:
|
|
For example: If there were a class `CDCover` defined this way:
|
|
|
|
|
|
|
@@ -517,20 +550,20 @@ class SongByTitle {
|
|
|
Values of type `Song` may be cast to `SongByArtist` or `SongByTitle` to get a
|
|
Values of type `Song` may be cast to `SongByArtist` or `SongByTitle` to get a
|
|
|
specific sort order.
|
|
specific sort order.
|
|
|
|
|
|
|
|
-### Interface input and output types
|
|
|
|
|
|
|
+### Interface inputs and outputs
|
|
|
|
|
|
|
|
-[Associated types and interface parameters](terminology.md#interface-parameters-and-associated-constants)
|
|
|
|
|
|
|
+[Associated constants and interface parameters](terminology.md#interface-parameters-and-associated-constants)
|
|
|
allow function signatures to vary with the implementing type. The biggest
|
|
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").
|
|
|
|
|
|
|
+difference between these is that associated constants ("outputs") may be deduced
|
|
|
|
|
+from a type, and types can implement the same interface multiple times with
|
|
|
|
|
+different interface parameters ("inputs").
|
|
|
|
|
|
|
|
-#### Associated types
|
|
|
|
|
|
|
+#### Associated constants
|
|
|
|
|
|
|
|
-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:
|
|
|
|
|
|
|
+Expect parts of function signatures that vary in an interface to be associated
|
|
|
|
|
+constants by default. Since associated constants may be deduced, they are more
|
|
|
|
|
+convenient to use. Imagine a `Stack` interface. Different types implementing
|
|
|
|
|
+`Stack` will have different element types:
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
interface Stack {
|
|
interface Stack {
|
|
@@ -541,10 +574,10 @@ interface Stack {
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-`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.
|
|
|
|
|
|
|
+`ElementType` is an associated constant of the interface `Stack`. Types that
|
|
|
|
|
+implement `Stack` give `ElementType` a specific value that is some type (really,
|
|
|
|
|
+facet) 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
|
|
// ✅ This is allowed, since the type of the stack will determine
|
|
@@ -581,16 +614,17 @@ fn FindInVector[T:! type, U:! Equatable(T)](v: Vector(T), needle: U)
|
|
|
|
|
|
|
|
// ❌ This is forbidden. Since `U` could implement `Equatable`
|
|
// ❌ This is forbidden. Since `U` could implement `Equatable`
|
|
|
// multiple times, there is no way to determine the value for `T`.
|
|
// multiple times, there is no way to determine the value for `T`.
|
|
|
-// Contrast with `PeekAtTopOfStack` in the associated type example.
|
|
|
|
|
|
|
+// Contrast with `PeekAtTopOfStack` in the associated constant
|
|
|
|
|
+// example.
|
|
|
fn CompileError[T:! type, U:! Equatable(T)](x: U) -> T;
|
|
fn CompileError[T:! type, U:! Equatable(T)](x: U) -> T;
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
### Constraints
|
|
### Constraints
|
|
|
|
|
|
|
|
-Type-of-types can be further constrained using a `where` clause:
|
|
|
|
|
|
|
+Facet types can be further constrained using a `where` clause:
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
-fn FindFirstPrime[T:! Container where .Element == i32]
|
|
|
|
|
|
|
+fn FindFirstPrime[T:! Container where .Element = i32]
|
|
|
(c: T, i: i32) -> Optional(i32) {
|
|
(c: T, i: i32) -> Optional(i32) {
|
|
|
// The elements of `c` have type `T.Element`, which is `i32`.
|
|
// The elements of `c` have type `T.Element`, which is `i32`.
|
|
|
...
|
|
...
|
|
@@ -608,7 +642,7 @@ increase the knowledge that may be used in the body of the function to operate
|
|
|
on values of those types.
|
|
on values of those types.
|
|
|
|
|
|
|
|
Constraints are also used when implementing an interface to specify the values
|
|
Constraints are also used when implementing an interface to specify the values
|
|
|
-of associated types (and other associated constants).
|
|
|
|
|
|
|
+of associated constants.
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
class Vector(T:! Movable) {
|
|
class Vector(T:! Movable) {
|
|
@@ -623,8 +657,8 @@ parameters can have constraints to restrict when the implementation applies.
|
|
|
When multiple implementations apply, there is a rule to pick which one is
|
|
When multiple implementations apply, there is a rule to pick which one is
|
|
|
considered the most specific:
|
|
considered the most specific:
|
|
|
|
|
|
|
|
-- All type parameters in each `impl` declaration are replaced with question
|
|
|
|
|
- marks `?`. This is called the type structure of the `impl` declaration.
|
|
|
|
|
|
|
+- All parameters in each `impl` declaration are replaced with question marks
|
|
|
|
|
+ `?`. This is called the type structure of the `impl` declaration.
|
|
|
- Given two type structures, find the first difference when read from
|
|
- Given two type structures, find the first difference when read from
|
|
|
left-to-right. The one with a `?` is less specific, the one with a concrete
|
|
left-to-right. The one with a `?` is less specific, the one with a concrete
|
|
|
type name in that position is more specific.
|
|
type name in that position is more specific.
|