mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:30:12 +01:00
Move adapters from generics to class design docs (#7561)
Assisted-by: Gemini via Antigravity --------- Co-authored-by: Josh L <josh11b@users.noreply.github.com>
This commit is contained in:
+106
-2
@@ -57,6 +57,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Partial class type](#partial-class-type)
|
||||
- [Usage](#usage)
|
||||
- [Assignment with inheritance](#assignment-with-inheritance)
|
||||
- [Compatible types](#compatible-types)
|
||||
- [Adapters](#adapters)
|
||||
- [Destructors](#destructors)
|
||||
- [Access control](#access-control)
|
||||
- [Private access](#private-access)
|
||||
@@ -1648,6 +1650,102 @@ implement it for final types. However, following the
|
||||
we allow users to also implement assignment on extensible classes, even though
|
||||
it can lead to [slicing](https://en.wikipedia.org/wiki/Object_slicing).
|
||||
|
||||
### Compatible types
|
||||
|
||||
Two types are compatible if they have the same notional set of values and
|
||||
represent those values in the same way, even if they expose different APIs. The
|
||||
representation of a type describes how the values of that type are represented
|
||||
as a sequence of bits in memory. The set of values of a type includes properties
|
||||
that the compiler can't directly see, such as invariants that the type
|
||||
maintains.
|
||||
|
||||
We can't just say two types are compatible based on structural reasons. Instead,
|
||||
we have specific constructs that create compatible types from existing types in
|
||||
ways that encourage preserving the programmer's intended semantics and
|
||||
invariants, such as implementing the API of the new type by calling (public)
|
||||
methods of the original API, instead of accessing any private implementation
|
||||
details.
|
||||
|
||||
Casting a value between compatible types is safe without any dynamic checks or
|
||||
danger of [object slicing](https://en.wikipedia.org/wiki/Object_slicing).
|
||||
|
||||
#### Adapters
|
||||
|
||||
An adapter creates a new type compatible with an existing type, but with a
|
||||
different API. Adapters are defined by using the `adapt` keyword inside a
|
||||
`class` definition:
|
||||
|
||||
```carbon
|
||||
class Song {
|
||||
fn Title(self) -> String;
|
||||
}
|
||||
class SongByTitle {
|
||||
adapt Song;
|
||||
}
|
||||
```
|
||||
|
||||
The rules for adapters 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 methods, functions, interface implementations, and aliases, but
|
||||
not fields, base classes, or virtual functions. The specific implementations
|
||||
of virtual functions are part of the type representation, and so no virtual
|
||||
functions may be overridden in an adapter either.
|
||||
- The adapted type is compatible with the original type, and that relationship
|
||||
is an equivalence class, so `Song`, `SongByTitle`, and any other adapters of
|
||||
`Song` 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 conversion between
|
||||
these types.
|
||||
|
||||
Inside an adapter, the `Self` type matches the adapter. Members of the original
|
||||
type may be accessed by a cast:
|
||||
|
||||
```carbon
|
||||
class SongByTitle {
|
||||
adapt Song;
|
||||
fn Less(self, rhs: Self) -> bool {
|
||||
return (self as Song).Title() < (rhs as Song).Title();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An adapter can also preserve the API and interface implementations of the original
|
||||
type using `extend adapt`. For details on how an extending adapter implements
|
||||
interfaces that are implemented for the adapted type, as well as applications of adapters to generics, see
|
||||
[Adapting types](/docs/design/generics/details.md#adapting-types) in the generics
|
||||
design.
|
||||
|
||||
**Comparison with other languages:** This is similar to the Rust idiom called
|
||||
"newtype", which is used to implement traits on types while avoiding
|
||||
[coherence](/docs/design/generics/terminology.md#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.
|
||||
|
||||
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`.
|
||||
|
||||
> **Future work:** We should have some way of restricting the casts between a type
|
||||
> and an adapter to address this use case. One possibility would be to add the
|
||||
> keyword `private` before `adapt`, so you might write
|
||||
> `extend private adapt Date;`.
|
||||
|
||||
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).
|
||||
|
||||
### Destructors
|
||||
|
||||
Every non-abstract type is _destructible_, meaning has a defined destructor
|
||||
@@ -1765,8 +1863,7 @@ interface Allocator {
|
||||
|
||||
To pass a pointer to a base class without a virtual destructor to a
|
||||
checked-generic function expecting a `Deletable` type, use the
|
||||
`UnsafeAllowDelete`
|
||||
[type adapter](/docs/design/generics/details.md#adapting-types).
|
||||
`UnsafeAllowDelete` [type adapter](#adapters).
|
||||
|
||||
```
|
||||
class UnsafeAllowDelete(T: Concrete) {
|
||||
@@ -2323,6 +2420,10 @@ the type of `U.x`."
|
||||
- [Nominal data class](/proposals/p000722-nominal-classes-and-methods.md#nominal-data-class)
|
||||
- [Let constants](/proposals/p000722-nominal-classes-and-methods.md#let-constants)
|
||||
|
||||
- [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731)
|
||||
|
||||
- [`adaptor` instead of `adapter`](/proposals/p000731-generics-details-2-adapters-associated-types-parameterized-interfaces.md#adaptor-instead-of-adapter)
|
||||
|
||||
- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777)
|
||||
|
||||
- [Classes are final by default](/proposals/p000777-inheritance.md#classes-are-final-by-default)
|
||||
@@ -2376,6 +2477,8 @@ the type of `U.x`."
|
||||
|
||||
- [Use `extends` instead of `extend`](/proposals/p002760-consistent-class-and-interface-syntax.md#use-extends-instead-of-extend)
|
||||
- [List base class in class declaration](/proposals/p002760-consistent-class-and-interface-syntax.md#list-base-class-in-class-declaration)
|
||||
- [Continue to use `adapter` or `adaptor` instead of `adapt`](/proposals/p002760-consistent-class-and-interface-syntax.md#continue-to-use-adapter-or-adaptor-instead-of-adapt)
|
||||
- [Use some other syntax for extending adapters](/proposals/p002760-consistent-class-and-interface-syntax.md#use-some-other-syntax-for-extending-adapters)
|
||||
|
||||
- [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017)
|
||||
|
||||
@@ -2401,6 +2504,7 @@ the type of `U.x`."
|
||||
- [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257)
|
||||
- [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561)
|
||||
- [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722)
|
||||
- [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731)
|
||||
- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777)
|
||||
- [#875: Principle: Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875)
|
||||
- [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981)
|
||||
|
||||
@@ -153,7 +153,7 @@ Lossy conversions between `iN` or `uN` and `iM` or `uM` are not supported with
|
||||
The following conversion is supported by `as`:
|
||||
|
||||
- `T` -> `U` if `T` is
|
||||
[compatible](../generics/terminology.md#compatible-types) with `U`.
|
||||
[compatible](/docs/design/classes.md#compatible-types) with `U`.
|
||||
|
||||
**Future work:** We may need a mechanism to restrict which conversions between
|
||||
adapters are permitted and which code can perform them. Some of the conversions
|
||||
@@ -162,7 +162,7 @@ permitted by this rule may only be allowed in certain contexts.
|
||||
## Extensibility
|
||||
|
||||
Explicit casts can be defined for user-defined types such as
|
||||
[classes](../classes.md) by implementing the `As` interface:
|
||||
[classes](/docs/design/classes.md) by implementing the `As` interface:
|
||||
|
||||
```
|
||||
interface As(Dest: type) {
|
||||
|
||||
@@ -46,7 +46,7 @@ These last two cases are highlighted as concerns in Rust in
|
||||
Since Carbon is bundling interface implementations into types, for the
|
||||
convenience and expressiveness that provides, we satisfy those use cases by
|
||||
giving the user control over the type of a value. This means having facilities
|
||||
for defining new [compatible types](terminology.md#compatible-types) with
|
||||
for defining new [compatible types](/docs/design/classes.md#compatible-types) with
|
||||
different interface implementations, and casting between those types as needed.
|
||||
|
||||
## The "Hashtable Problem"
|
||||
|
||||
@@ -45,7 +45,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Use case: Defining an impl for use by other types](#use-case-defining-an-impl-for-use-by-other-types)
|
||||
- [Use case: Private impl](#use-case-private-impl)
|
||||
- [Use case: Accessing interface names](#use-case-accessing-interface-names)
|
||||
- [Future work: Adapter with stricter invariants](#future-work-adapter-with-stricter-invariants)
|
||||
- [Associated constants](#associated-constants)
|
||||
- [Associated functions](#associated-functions)
|
||||
- [Associated facets](#associated-facets)
|
||||
@@ -317,7 +316,7 @@ Assert(p1.Add(p1) == p2);
|
||||
```
|
||||
|
||||
For more on how `extend` affects member access, see
|
||||
[member access](../expressions/member_access.md#extend).
|
||||
[member access](/docs/design/expressions/member_access.md#extend).
|
||||
|
||||
Without `extend`, those methods may only be accessed with
|
||||
[qualified member names and compound member access](#qualified-member-names-and-compound-member-access):
|
||||
@@ -1433,7 +1432,7 @@ fn DoHashAndEquals[T: Hashable](x: T) {
|
||||
> interface doesn't impl the interfaces it extends, as adopted in
|
||||
> [#5168: Forward `impl` declaration of an incomplete interface](/proposals/p005168-forward-impl-declaration-of-an-incomplete-interface.md).
|
||||
> Should link to
|
||||
> [`extend` in member access](../expressions/member_access.md#extend).
|
||||
> [`extend` in member access](/docs/design/expressions/member_access.md#extend).
|
||||
|
||||
When implementing an interface, we allow implementing the aliased names as well.
|
||||
In the case of `Hashable` above, this includes all the members of `Equatable`,
|
||||
@@ -1800,10 +1799,9 @@ be detected in function overloading.
|
||||
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
|
||||
switch the type of a value to access different interface implementations. Carbon
|
||||
therefore provides 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:
|
||||
therefore provides [adapters](/docs/design/classes.md#adapters) as a way to create new types
|
||||
[compatible with](/docs/design/classes.md#compatible-types) existing types with different
|
||||
APIs, in particular with different interface implementations:
|
||||
|
||||
```carbon
|
||||
interface Printable {
|
||||
@@ -1835,60 +1833,11 @@ class FormattedSongByTitle {
|
||||
This allows developers 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:
|
||||
(as in `FormattedSongByTitle`).
|
||||
|
||||
- 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 methods, functions, interface implementations, and aliases, but
|
||||
not fields, base classes, or virtual functions. The specific implementations
|
||||
of virtual functions are part of the type representation, and so no virtual
|
||||
functions may be overridden in an adapter either.
|
||||
- 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 conversion between
|
||||
these types.
|
||||
|
||||
Inside an adapter, the `Self` type matches the adapter. Members of the original
|
||||
type may be accessed either by a cast:
|
||||
|
||||
```carbon
|
||||
class SongByTitle {
|
||||
adapt Song;
|
||||
extend impl as Ordered {
|
||||
fn Less(self, rhs: Self) -> bool {
|
||||
return (self as Song).Title() < (rhs as Song).Title();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
or using a qualified member access expression:
|
||||
|
||||
```carbon
|
||||
class SongByTitle {
|
||||
adapt Song;
|
||||
extend impl as Ordered {
|
||||
fn Less(self, rhs: Self) -> bool {
|
||||
return self.(Song.Title)() < rhs.(Song.Title)();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Comparison with other languages:** This matches the Rust idiom called
|
||||
"newtype", which is used to implement traits on types while avoiding
|
||||
[coherence](terminology.md#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).
|
||||
For the definition of adapters, including what declarations can be added to an
|
||||
adapter, compatibility rules, member access, and casting between adapted types,
|
||||
see [adapters in the class design](/docs/design/classes.md#adapters).
|
||||
|
||||
### Adapter compatibility
|
||||
|
||||
@@ -1952,7 +1901,7 @@ 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 `extend` keyword
|
||||
before `adapt`, which
|
||||
[extends member access to lookup names in the adapted class](../expressions/member_access.md#extend)
|
||||
[extends member access to lookup names in the adapted class](/docs/design/expressions/member_access.md#extend)
|
||||
along with `impl` lookup:
|
||||
|
||||
```carbon
|
||||
@@ -2216,21 +2165,6 @@ fn Render(w: Window) {
|
||||
}
|
||||
```
|
||||
|
||||
### Future work: 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. One possibility would be to add the
|
||||
keyword `private` before `adapt`, so you might write
|
||||
`extend private adapt Date;`.
|
||||
|
||||
## Associated constants
|
||||
|
||||
> **TODO:** Update this section to reflect the new rules and guidance on
|
||||
@@ -2741,7 +2675,7 @@ member of another. The `where` operator is not associative, so a type expression
|
||||
using multiple must use round parens `(`...`)` to specify grouping.
|
||||
|
||||
The scope of a facet type formed by a `where` declaration
|
||||
[extends](../expressions/member_access.md#extend) the scope of its first
|
||||
[extends](/docs/design/expressions/member_access.md#extend) the scope of its first
|
||||
operand, and the resulting facet type is complete if that scope it extends is
|
||||
complete.
|
||||
|
||||
@@ -4007,7 +3941,7 @@ Given a type `U`, define the facet type `CompatibleWith(U)` as follows:
|
||||
|
||||
> `CompatibleWith(U)` is a facet type whose values are facets `T` such that
|
||||
> `T as type` and `U as type` are
|
||||
> [compatible types](terminology.md#compatible-types). That is values of `T` and
|
||||
> [compatible types](/docs/design/classes.md#compatible-types). That is values of `T` and
|
||||
> `U` as types can be cast back and forth without any change in representation
|
||||
> (for example `T` is an [adapter](#adapting-types) for `U`).
|
||||
|
||||
@@ -5475,7 +5409,7 @@ An incomplete `C` cannot be used in the following contexts:
|
||||
- ❌ `interface `...` { extend require impls C; }` or
|
||||
`constraint `...` { extend require impls C; }`
|
||||
- An `extend` declaration requires the target scope to be complete. See
|
||||
[`extend` in member access](../expressions/member_access.md#extend).
|
||||
[`extend` in member access](/docs/design/expressions/member_access.md#extend).
|
||||
- ❌ `T: C` ... `T impls A` where `T` is a checked binding, and `A` is an
|
||||
interface or named constraint different from `C`
|
||||
- Need to see the definition of `C` to see if it implies `A`.
|
||||
|
||||
@@ -526,8 +526,8 @@ cast from `T` to `CDCover`.
|
||||
|
||||
### Adapting types
|
||||
|
||||
Carbon has a mechanism called [adapting types](terminology.md#adapting-a-type)
|
||||
to create new types that are [compatible](terminology.md#compatible-types) with
|
||||
Carbon has a mechanism called [adapting types](/docs/design/classes.md#adapters)
|
||||
to create new types that are [compatible](/docs/design/classes.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.
|
||||
|
||||
|
||||
@@ -38,10 +38,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Member access](#member-access)
|
||||
- [Simple member access](#simple-member-access)
|
||||
- [Qualified member access expression](#qualified-member-access-expression)
|
||||
- [Compatible types](#compatible-types)
|
||||
- [Subtyping and casting](#subtyping-and-casting)
|
||||
- [Coherence](#coherence)
|
||||
- [Adapting a type](#adapting-a-type)
|
||||
- [Type erasure](#type-erasure)
|
||||
- [Archetype](#archetype)
|
||||
- [Extending an interface](#extending-an-interface)
|
||||
@@ -478,7 +476,7 @@ members of the interface as named members of the type. This means that the
|
||||
members of the interface are available by way of both
|
||||
[simple member access and qualified member access expressions](#member-access).
|
||||
See
|
||||
[how `extend` affects member access](../expressions/member_access.md#extend).
|
||||
[how `extend` affects member access](/docs/design/expressions/member_access.md#extend).
|
||||
|
||||
If a type implements an interface without extending, the members of the
|
||||
interface may only be accessed using
|
||||
@@ -522,22 +520,6 @@ member access expression `s1.(Comparable.Less)(s2)`.
|
||||
This form may be used to access any member of an interface implemented for a
|
||||
type, whether or not it [extends the implementation](#extending-an-impl).
|
||||
|
||||
## Compatible types
|
||||
|
||||
Two types are compatible if they have the same notional set of values and
|
||||
represent those values in the same way, even if they expose different APIs. The
|
||||
representation of a type describes how the values of that type are represented
|
||||
as a sequence of bits in memory. The set of values of a type includes properties
|
||||
that the compiler can't directly see, such as invariants that the type
|
||||
maintains.
|
||||
|
||||
We can't just say two types are compatible based on structural reasons. Instead,
|
||||
we have specific constructs that create compatible types from existing types in
|
||||
ways that encourage preserving the programmer's intended semantics and
|
||||
invariants, such as implementing the API of the new type by calling (public)
|
||||
methods of the original API, instead of accessing any private implementation
|
||||
details.
|
||||
|
||||
## Subtyping and casting
|
||||
|
||||
Both subtyping and casting are different names for changing the type of a value
|
||||
@@ -569,14 +551,14 @@ make it clear that the data representation of the value is not changing, just
|
||||
its type as reflected in the API available to manipulate the value.
|
||||
|
||||
Casting is indicated explicitly by way of some syntax in the source code. You
|
||||
might use a cast to switch between [type adaptations](#adapting-a-type), or to
|
||||
might use a cast to switch between [type adaptations](/docs/design/classes.md#adapters), or to
|
||||
be explicit where an implicit conversion would otherwise occur. For now, we are
|
||||
saying "`x as y`" is the provisional syntax in Carbon for casting the value `x`
|
||||
to the type `y`. Note that outside of generics, the term "casting" includes any
|
||||
explicit type change, including those that change the data representation.
|
||||
|
||||
In contexts where an expression of one type is provided and a different type is
|
||||
required, an [implicit conversion](../expressions/implicit_conversions.md) is
|
||||
required, an [implicit conversion](/docs/design/expressions/implicit_conversions.md) is
|
||||
performed if it is considered safe to do so. Such an implicit conversion, if
|
||||
permitted, always has the same meaning as an explicit cast.
|
||||
|
||||
@@ -607,27 +589,6 @@ This is enforced using two kinds of rules:
|
||||
The rationale for Carbon choosing coherence and alternatives considered may be
|
||||
found in [this appendix](appendix-coherence.md)
|
||||
|
||||
## Adapting a type
|
||||
|
||||
A type can be adapted by creating a new type that is
|
||||
[compatible](#compatible-types) with an existing type, but has a different API.
|
||||
In particular, the new type might implement different interfaces or provide
|
||||
different implementations of the same interfaces.
|
||||
|
||||
Unlike extending a type (as with C++ class inheritance), you are not allowed to
|
||||
add new data fields onto the end of the representation -- you may only change
|
||||
the API. This means that it is safe to [cast](#subtyping-and-casting) a value
|
||||
between those two types without any dynamic checks or danger of
|
||||
[object slicing](https://en.wikipedia.org/wiki/Object_slicing).
|
||||
|
||||
This is called "newtype" in Rust, and is used for capturing additional
|
||||
information in types to improve type safety by moving some checking to compile
|
||||
time ([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))
|
||||
and as a workaround for
|
||||
[Rust's orphan rules for coherence](https://github.com/Ixrec/rust-orphan-rules#why-are-the-orphan-rules-controversial).
|
||||
|
||||
## Type erasure
|
||||
|
||||
"Type erasure" is where a type's API is replaced by a subset. Everything outside
|
||||
@@ -664,7 +625,7 @@ An interface can be extended by defining an interface that includes the full API
|
||||
of another interface, plus some additional API. Types implementing the extended
|
||||
interface should automatically be considered to have implemented the narrower
|
||||
interface. See
|
||||
[how `extend` affects member access](../expressions/member_access.md#extend).
|
||||
[how `extend` affects member access](/docs/design/expressions/member_access.md#extend).
|
||||
|
||||
## Dynamic-dispatch witness table
|
||||
|
||||
@@ -863,7 +824,7 @@ express, for example:
|
||||
element type.
|
||||
- An interface may define an associated facet that needs to be constrained to
|
||||
implement some interfaces.
|
||||
- This type must be [compatible](#compatible-types) with another type. You
|
||||
- This type must be [compatible](/docs/design/classes.md#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.
|
||||
|
||||
|
||||
@@ -991,7 +991,7 @@ Each of the conversions described in this section is explicit if and only if it
|
||||
invokes another explicit type conversion. Otherwise, it is implicit.
|
||||
|
||||
A type conversion of an expression with primitive extended type to a
|
||||
[compatible type](generics/terminology.md#compatible-types) just re-interprets
|
||||
[compatible type](classes.md#compatible-types) just re-interprets
|
||||
the expression's result with a new type, so it requires no run-time work, and
|
||||
has the same category as the input expression.
|
||||
|
||||
|
||||
@@ -187,8 +187,8 @@ class C {
|
||||
Each member of `C` with a distinct name will have a corresponding type (like
|
||||
`__TypeOf_C_F`) and value of that type (like `__C_F`). There are two more types
|
||||
for each member function (either static class function or method), though, that
|
||||
[adapt](/docs/design/generics/terminology.md#adapting-a-type) `C` and represent
|
||||
the type of binding that member with either a `C` value or variable.
|
||||
[adapt](/docs/design/classes.md#adapters) `C` and represent the type of binding
|
||||
that member with either a `C` value or variable.
|
||||
|
||||
```carbon
|
||||
class __TypeOf_C_F {}
|
||||
|
||||
Reference in New Issue
Block a user