diff --git a/docs/design/lexical_conventions/words.md b/docs/design/lexical_conventions/words.md index 913dfb472b75c..f1034d90128c7 100644 --- a/docs/design/lexical_conventions/words.md +++ b/docs/design/lexical_conventions/words.md @@ -37,6 +37,7 @@ in Unicode Normalization Form C (NFC). The following words are interpreted as keywords: - `abstract` +- `adapt` - `addr` - `alias` - `and` @@ -51,8 +52,7 @@ The following words are interpreted as keywords: - `continue` - `default` - `else` -- `extends` -- `external` +- `extend` - `final` - `fn` - `for` @@ -77,6 +77,7 @@ The following words are interpreted as keywords: - `partial` - `private` - `protected` +- `require` - `return` - `returned` - `Self` diff --git a/proposals/p2760.md b/proposals/p2760.md new file mode 100644 index 0000000000000..fa45337fb6303 --- /dev/null +++ b/proposals/p2760.md @@ -0,0 +1,838 @@ +# Consistent `class` and `interface` syntax + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/2760) + + + +## Table of contents + +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) + - [Class inheritance](#class-inheritance) + - [Class implementing an interface](#class-implementing-an-interface) + - [Class conditional implementation](#class-conditional-implementation) + - [Adapters](#adapters) + - [Interfaces](#interfaces) +- [Future work: mixins](#future-work-mixins) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + - [Use `extends` instead of `extend`](#use-extends-instead-of-extend) + - [Allow interfaces to `require` another interface without writing `Self impls`](#allow-interfaces-to-require-another-interface-without-writing-self-impls) + - [Allow other kinds of `where` clauses after `require`](#allow-other-kinds-of-where-clauses-after-require) + - [Continue to use `impl as` for interface requirements](#continue-to-use-impl-as-for-interface-requirements) + - [Continue to use `adapter` or `adaptor` instead of `adapt`](#continue-to-use-adapter-or-adaptor-instead-of-adapt) + - [Use some other syntax for extending adapters](#use-some-other-syntax-for-extending-adapters) + - [Continue to have some term for "external" or non-extended implementations](#continue-to-have-some-term-for-external-or-non-extended-implementations) + - [More direct support for conditionally implementing internal interfaces](#more-direct-support-for-conditionally-implementing-internal-interfaces) + - [Use `external` consistently instead of `extend`](#use-external-consistently-instead-of-extend) + - [Use `mix` keyword for mixins](#use-mix-keyword-for-mixins) + - [Allow more control over access to mixins](#allow-more-control-over-access-to-mixins) + - [List base class in class declaration](#list-base-class-in-class-declaration) + + + +## Abstract + +Update syntax of `class` and `interface` definitions to be more consistent. +Constructs that add names to the class or interface from another definition are +always prefixed by the `extend` keyword. + +Implements the decisions in: + +- [#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995), +- [#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159), +- [#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580), + and +- [#2770: Terminology for internal and external implementations](https://github.com/carbon-language/carbon-lang/issues/2770). + +## Problem + +Classes and adapters, prior to this proposal, use `impl` to say that an +interface is +[implemented internally](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/terminology.md#internal-impl), +which means that the names that are members of the interface are included as +names of the class. The keyword `external` is added to indicate the names should +not be included. Interfaces and named constraints, in contrast, use `impl` to +mean another interface is required, but its names are not included. Instead, to +include the names, the `extends` keyword used instead of `impl`. + +| Include names: | Yes | No | +| ------------------------- | --------- | --------------- | +| `class`, `adapter` | `impl` | `external impl` | +| `interface`, `constraint` | `extends` | `impl` | + +In the time since this syntax has been introduced, we have found `external` in +particular easy to accidentally omit. + +In addition to resolving this inconsistency, it would be an advantage if readers +of a class could quickly scan the definition to identify other places to look +for members that contribute to the class' API. + +## Background + +These proposals that defined the syntax for these entities that are being +modified: + +- [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) + defined the syntax for classes implementing interfaces, internally or + externally, and the syntax for named constraints (then "structural + interfaces") and interfaces requiring or extending other interfaces. +- [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) + defined the syntax for adapters and extending adapters. +- [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) + allowed forward declaration of implementations, so internal `impl` + declarations may appear outside of a class definition, and `external impl` + declarations may appear inside. +- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) + defined the syntax for a class to extend a base class. + +No proposal so far has defined how forward declarations work for classes. The +rule used for forward `interface`, `constraint`, and `impl` declarations is that +the declaration part of the definition is everything up to the opening `{` of +the definition body. See +[the forward declaration section of the Generics details design doc](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-declarations-and-cyclic-references) +added in proposal +[#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084). + +This proposal incorporates the decisions made in these question-for-leads +issues: + +- [#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995), +- [#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159), +- [#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580), + and +- [#2770: Terminology for internal and external implementations](https://github.com/carbon-language/carbon-lang/issues/2770). + +Some of thinking around the resolution of #995 was documented in +[issue #2293: reconsider syntax for internal / external implementation of interfaces](https://github.com/carbon-language/carbon-lang/issues/2293), +which was closed as a duplicate of #995. + +In addition to modifying syntax from previous proposals, +[#995](https://github.com/carbon-language/carbon-lang/issues/995) also gives a +syntax for using a mixin in a class. Mixins are described as a use case in +[#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561), +but have not been added in any proposal. Question-for-leads issue +[#1000: Mixins: base classes or data members?](https://github.com/carbon-language/carbon-lang/issues/1000), +does state that a class will treat a mixin syntactically like a data member +instead of a base class. + +## Proposal + +Any declaration that adds the names from another entity shall start with the +(new) `extend` keyword. This includes: + +- _Inheritance_: A class now indicates that it inherits from a base class + using an + + > `extend base:` _base-class_ `;` + + declaration inside the class definition. The `extend` keyword indicates that + the API of the base class is included. + +- _Adapters_: Adapter types are now declared as a class, with an + + > [ `extend` ] `adapt` _adapted-class_ `;` + + declaration inside the definition, as an alternative to a base class + declaration. The optional `extend` keyword controls whether the API of the + adapted class is included. + +- _Implementations_: Internal implementations are marked with the `extend` + keyword on the declaration inside the class. Only the declaration inside the + class, which is required for internal implementations, uses the `extend` + keyword. External implementations are not marked. + + > [ `extend` ] `impl` ... + +- _Interfaces_: The `extends` declaration in an interface definition is + replaced by an `extend` declaration, with no change except removing the `s` + from the end of the keyword. Other interface requirements are now written + using a `require` declaration, with a constraint that matches a `where` + clause. This means + + > `impl as` _required-interface_ `;` + + will now be written as + + > `require Self impls` _required-interface_ `;` + + and + + > `impl` _type-expression_ `as` _required-interface_ `;` + + will now be written as + + > `require` _type-expression_ `impls` _required-interface_ `;` + + For now, only the `impls` forms of `where` clauses are permitted after + `require`. + +In summary: + +| Before | After | +| ------------------------------- | --------------------------------------- | +| `class D extends B { ... }` | `class D { extend base: B; ... }` | +| `external impl C as Sub;` | `impl C as Sub;` | +| `class C { impl as Sortable; }` | `class C { extend impl as Sortable; }` | +| `adapter A for C { ... }` | `class A { adapt C; ... }` | +| `adapter A extends C { ... }` | `class A { extend adapt C; ... }` | +| `interface I { impl as J; }` | `interface I { require Self impls J; }` | +| `interface I { impl T as J; }` | `interface I { require T impls J; }` | +| `interface I { extends J; }` | `interface I { extend J; }` | + +None of `adapter`, `extends`, `external` will continue to be keywords. To match +these changes, "internal implementations" will now be referred to as "extended +implementations," and we will no longer use "external" to refer to +implementations. + +In addition, we drop the syntax for conditionally implemented internal +interfaces. Instead, an external interface implementation can be combined with +aliases to the members of the interface. + +## Details + +### Class inheritance + +What was previously written: + +``` +base class B; +class D extends B; +class D extends B { + ... +} +``` + +is now written: + +``` +base class B; +class D; +class D { + extend base: B; + ... +} +``` + +An extend base class declaration may appear in the body of a class definition, +and has this form: + +> `extend` `base` `:` _type-expression_ `;` + +The `extend base: B;` declaration must appear before any other data member +declaration, including any [mixin declaration](#future-work-mixins), once those +are added. This reflects both the importance of the information, and the fact +that the base subobject appears first in the memory layout of objects. + +Note that `base` is already a keyword, for example used in `base class` +declarations. The colon in `base: B` is to indicate that `base` acts like a data +member for +[purposes of initialization](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#constructors). + +This makes the part of a class definition that is used in forward declarations +be exactly the part before the curly braces (`{`...`}`). Before this proposal, +class forward declarations would exclude the `extends` clause from the the first +line of the class definition. This change makes classes consistent with other +entities that may be forward declared. + +### Class implementing an interface + +What was previously written: + +``` +interface Sortable; +interface Add; +interface Sub; +class C; + +// Forward declaration says whether external. +impl C as Sortable; +external impl C as Add; + +class C { + // Internal impl contributes to the API. + impl as Sortable; + + // External impl of an operator. + external impl as Add; +} + +// External impl of an operator. +external impl C as Sub; + +// Definition of `impl` declared earlier. +impl C as Sortable { ... } +external impl C as Add { ... } +``` + +is now written: + +``` +interface Sortable; +interface Add; +interface Sub; +class C; + +// Forward declaration same whether extended or not. +impl C as Sortable; +impl C as Add; + +class C { + // Extended impl contributes to the API. + extend impl as Sortable; + + // (Non-extended) Impl of an operator. + impl as Add; +} + +// (Non-extended) Impl of an operator. +impl C as Sub; + +// Definition of `impl` declared earlier. +impl C as Sortable { ... } +impl C as Add { ... } +``` + +Whether an interface is extended or not is now only reflected in its declaration +inside the class body, not in any declaration or definition outside. + +An `impl` declaration, with this proposal, must have one of these two forms: + +- Without an `extend` keyword prefix, used for non-extended `impl` + declarations and for all `impl` declarations outside of a class body: + + + > `impl` [`forall` `[` _deduced-parameters_ `]`] [_type-expression_] `as` + > _facet-type-expression_ (`;`|`{` _impl-body_ `}`) + + The _type-expression_ is required outside of a class body, otherwise it + defaults to `Self`. + +- With an `extend` keyword prefix, to indicate this implementation is + extended, only in a class body: + + > `extend` `impl` `as` _facet-type-expression_ (`;`|`{` _impl-body_ `}`) + + Note that this form does not allow either a `forall` clause nor a + _type-expression_ before the `as` keyword. This reflects the restriction + that + [wildcard `impl` declarations must never be extended (formerly: "always be external")](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#wildcard-impl-declarations), + and that this proposal removes support for + [extended (formerly "internal") conditional implementation](#class-conditional-implementation). + +### Class conditional implementation + +We remove direct support for conditionally implemented extended (formerly +"internal") interfaces, called +[conditional conformance](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#conditional-conformance). +We can work around this restriction by using an non-extended interface +implementation and aliases to the members of the interface. + +What was previously written: + +``` +interface Printable { + fn Print[self: Self](); +} + +class Vector(T:! type) { + // ... + + impl forall [U:! Printable] Vector(U) as Printable { + fn Print[self: Self](); + } +} +``` + +is now written: + +``` +interface Printable { + fn Print[self: Self](); +} + +class Vector(T:! type) { + // ... + alias Print = Printable.Print; +} + +impl forall [U:! Printable] Vector(U) as Printable { + fn Print[self: Self](); +} +``` + +The way this works is that `Vector.Print` is equivalent to +`Vector.(Printable.Print)`, which may or may not be defined. The name +`Vector.Print` can no longer be conditional, and the meaning of that name is +fixed. However, the implementation of `Printable` for `Vector(T)` may not exist +for some types `T`. + +### Adapters + +What was previously written: + +``` +class C; +// Forward declarations of adapters. +adapter A for C; +adapter E extends C; + +// Definitions of an adapter. +adapter A for C { + ... +} +// Definition of an extending adapter. +adapter E extends C { + ... +} +``` + +is now written: + +``` +class C; +// Forward declarations of adapters. +class A; +class E; + +// Definitions of an adapter. +class A { + adapt C; + ... +} +// Definition of an extending adapter. +class E { + extend adapt C; + ... +} +``` + +Note: + +- Adapters are now a special case of classes, not a distinct top-level + declaration. +- Classes with `adapt` still must not contain anything that was previously + forbidden for adapters: no fields, no base class, no virtual methods, no + implementations of virtual methods, and so on. +- The `adapt` declaration must appear before + [mixin declarations](#future-work-mixins), if any. +- The syntax for an `adapt` declaration inside a class body is: + + > [`extend`] `adapt` _type-expression_ `;` + +### Interfaces + +What was previously written: + +``` +interface A { let T:! Type; } +interface B { let U:! Type; } +interface C(V:! Type) { } + +interface I { + // `A`'s interface is incorporated into `I`: + extends A where .T = i32; + + // No impact on `I`s interface, but an + // implementation must exist: + impl as B where .U = i32; + + // Implementation must exist on another type: + impl i32 as C(Self); +} +``` + +is now written: + +``` +interface A { let T:! Type; } +interface B { let U:! Type; } +interface C(V:! Type) { } + +interface I { + // `A`'s interface is incorporated into `I`: + extend A where .T = i32; + + // No impact on `I`s interface, but an + // implementation must exist: + require Self impls B where .U = i32; + + // Implementation must exist on another type: + require i32 impls C(Self); +} +``` + +Notes: + +- The same change applies to named constraints, and is intended to be used in + the future for named predicates used as template constraints. +- One syntax for constraints in either `where` clauses or `require` + declarations. +- Want to open up the syntax to expressing more general constraints. +- Syntax for a `require` declaration in an interface or named constraint: + + > `require` _type-expression_ `impls` _facet-type-expression_ `;` + + As + [with `impl`...`as` declarations before](/docs/design/generics/details.md#interface-requiring-other-interfaces-revisited), + a `require` declaration must use `Self`, either to the left or right of + `impls`. Note that `require` only supports this subset of `where` clause + expressions. Adding other kinds of constraints is future work. + +- Syntax for an `extend` declaration in an interface or named constraint: + + > `extend` _facet-type-expression_ `;` + +## Future work: mixins + +Mixins have not been defined in a proposal so far. However, part of the process +of resolving +[issue #995](https://github.com/carbon-language/carbon-lang/issues/995) was +deciding on a syntax for including a mixin in a class. This was done in order to +make sure that class declarations that included names from another entity were +treated consistently, for example always starting with the `extend` keyword. + +``` +// Mixin declarations and definitions are +// outside the scope of this proposal. +mixin M1; +mixin M2; + +class C { + // Mixing in mixin M1 + extend m: M1; + + // Mixing in mixin M2. This member is not named. + // Initialized using `M2.MakeDefault()`. + extend _: M2 = M2.MakeDefault(); + + // Alternative to the above `M2` that uses a + // private name instead of no name: + extend private m2: M2; +} +``` + +The declaration that a class uses a mixin is called a "mix" declaration. The +syntax of a mix declaration is: + + +> `extend` [`private`|`protected`] (`_`|_id_) `:` _mixin-expression_ [`=` +> _initializer-expression_] `;` + +The _id_ part of the mix declaration defines the name assigned to that mixin +subobject. This name is may be used to access members of the mixin and to +initialize the mixin in a constructor for the class. The optional `private` or +`protected` access specifier controls the access to this name. + +With this proposal, base class declarations appear in the body of the class +definition, like data members, so decision of whether mixins are more like base +classes or data members of issue +[#1000: Mixins: base classes or data members?](https://github.com/carbon-language/carbon-lang/issues/1000) +is less significant. Like base classes, the mix declaration syntax begins with +`extend`. Like data members, a class may have multiple mix declarations and they +may be intermixed with field declarations. The layout of the memory of an object +reflects the order of the declarations in the class body, defining the order of +the mixin and field subobjects. + +## Rationale + +The main reason for the new syntax is consistency and simplification: + +- The use of the `extend` keyword is the consistent way to mark what other + entities are consulted during name lookup. +- The `class` declaration is simplified by moving more into the definition + body. +- Making adapters a kind of class removes a kind of top-level declaration, a + simplification, and matches how base classes are declared, a consistency. +- Dropping the class conditional implementation is a simplification. + +These consistency and simplification improvements help: + +- ease the implementation of + [language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) + by reducing the size of the language, and providing regularities + implementations can use to reuse code or more easily identify relevant parts + of the code for queries; +- make Carbon code + [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). +- teaching the language, since there is less to learn. + +## Alternatives considered + +### Use `extends` instead of `extend` + +Keyword `extend` was chosen over `extends` to parallel `impl`, a declaration, +instead of `impls`, a binary predicate, decided in +[issue #2495](https://github.com/carbon-language/carbon-lang/issues/2495) and +accepted in +[proposal #2483](https://github.com/carbon-language/carbon-lang/pull/2483). + +We chose to use `require` instead of `requires` and `adapt` instead of `adapts` +for the same consistency. + +### Allow interfaces to `require` another interface without writing `Self impls` + +We +[considered](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1418387971) +allowing `interface I { require J; }` as a short-hand for +`interface I { require Self impls J; }`. This is something we would consider +adding in the future based on experience with the current approach, but for now +we wanted to maintain consistency with the constraint syntax of `where` clauses. + +This decision and rationale was described in +[this comment in #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1416659207). + +### Allow other kinds of `where` clauses after `require` + +The decision on +[#995](https://github.com/carbon-language/carbon-lang/issues/995), see +[1](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1439336141) +and +[2](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1496781087), +called for the same constraint syntax after `require` as we are using after +`where`. This proposal _only_ allows `impls` clauses after `require`, since +there were concerns about the other kinds of clauses: + +- We had questions about what syntax to use to refer to members of the + interface, such as associated types, to constrain them. Must they start with + `Self.` or `.`? See + [this comment thread on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1213789364). +- Modifying the constraints on an associated type after the declaration for + that associated type was not something we clearly wanted to allow. Allowing + that would mean having to read the whole interface definition body to + understand a single member. +- It was unclear if rewrite constraints (using `=` instead of `==`), would be + allowed in a `require` declaration, and if they were, whether they would be + changed into equality constraints (as if they were declared using `==`). See + [this comment on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1213791304). +- It would have provided more different ways of declaring equivalent + interfaces. This concern was raised + [in this comment on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1214571215). + +For now, we only needed to replace the existing uses of `impl as` constraints, +which had none of these concerns. We did not want to block this proposal, so we +made sure the `require` clauses were consistent with that _subset_ of `where` +clauses. + +### Continue to use `impl as` for interface requirements + +There were a few reasons motivating the change to use the new `require` +declarations in interfaces and named constraints, instead of using `impl as` to +match how a type could satisfy that requirement. These mostly came down to some +observed breaks in the parallel structure between the requirement in interfaces +and the satisfaction of that requirement in types. + +- Whether an interface `I` extends or just requires another interface `J` is + independent of whether a type implementing `I` extends or just implements + `J`. +- If `R` requires interface `I`, the implementation of `R` for a type won't + have the implementation of `I` as a nested sub-block. +- With the change in + [#2173: Associated constant assignment versus equality](https://github.com/carbon-language/carbon-lang/pull/2173), + the behavior of `impl as` in interfaces is different from in classes with + respect to rewrites of associated types, motivating a change to make those + look more different. + +Furthermore, we had a desire to be able to express the full range of constraints +in `where` clauses in named constraints, and we wanted the transformation from a +`where` clause to a named constraint to be straightforward. We also wanted the +syntax for constraints to be the same between interfaces and named constraints, +at least for all constraints that were allowed in both. + +This was discussed in +[#generics-and-templates on 2023-01-30](https://discord.com/channels/655572317891461132/941071822756143115/1069691751968817283) +and in +[issue #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1412478644). + +### Continue to use `adapter` or `adaptor` instead of `adapt` + +We didn't want to allow both `adapter` and `adaptor`, since that adds complexity +for readers and tooling, but neither seemed clearly dominant enough in usage to +pick one over the other. By moving the declaration into the body of the class +definition, we were able to switch from the noun form to the verb form of +`adapt`, which doesn't have an alternate form in common usage. See +[#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159) +for the discussion. + +We also considered using `adapts` in a class declaration, as in +`class PlayableSong adapts Song { ... }`, see +[this comment in #1159](https://github.com/carbon-language/carbon-lang/issues/1159#issuecomment-1316669416). +This would have also worked, but was not consistent with our resolution of +[#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995). + +### Use some other syntax for extending adapters + +In the +[open discussion on 2023-02-27](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.9steyq834zuq), +we discussed some alternatives to `extend adapt` _adapted-class_ `;`: + +- Adding `and` to make it read more like fluent English: + + > `extend and adapt` _adapted-class_ `;` + + However, this felt arbitrary and not compositional. + +- Make the extending and adapting be separate declarations: + + > `extend` _adapted-class_ `;`
`adapt` _adapted-class_ `;` + + This felt too repetitive. + +- Make the only way to make an extending adapter be trying to inherit from a + final base class + + > `extend base:` _adapted-class_ `;` + + However, this meant using the same syntax for two different things that + could only be distinguished by looking at the declaration of the adapted + class, which could be far away. It also would have meant making an extending + adapter of a non-final class much more cumbersome. + +Ultimately, we decided that `extend adapt` would be the most compositional way +of combining `extend` and `adapt`, so that is what users would expect. Reading +like natural English was not considered essential. + +### Continue to have some term for "external" or non-extended implementations + +The fact that there were some different rules for external implementations was +brought up in +[this post in #2770](https://github.com/carbon-language/carbon-lang/issues/2770#issuecomment-1509288478). +However, +[this reply](https://github.com/carbon-language/carbon-lang/issues/2770#issuecomment-1515522070) +pointed out those rules could be clearly stated in terms of where you are +allowed to write `extend`. That same post made the convincing argument that to +get the maximum benefit of the decision on +[#995](https://github.com/carbon-language/carbon-lang/issues/995), we should +treat `extend` and `impl` as separate orthogonal concepts as much as possible. + +### More direct support for conditionally implementing internal interfaces + +We considered two different ways of more directly supporting conditionally +extending a class by an interface: + +- Prior to this proposal, both name lookup and the implementation were + conditional on the same condition. This has the disadvantage of entangling + the concerns of name lookup and implementation, and was considered a complex + and difficult model. +- We also considered fully separating these features, and allowing a type to + separately extend its API with an interface and then only conditionally + implement that interface. This would allow name lookup to succeed + unconditionally, but in cases where the names were in fact not implemented + it would produce an error. + +Both of these approaches would have required some support for expressing this in +the new syntax. None of the syntactic approaches we considered were found to be +satisfactory. By making name lookup never conditional, it made it much easier to +have a consistent marker for declarations that extended name lookup. + +Ultimately, the alternative of not having a dedicated syntax to support this +case seemed the simplest in the short term, given the workaround of conditional +external (non-extending) implementation paired with aliases that provide the +name lookup, unconditionally. We can always add dedicated syntax later, given +sufficient motivating information. + +The options were considered in +[#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580). + +### Use `external` consistently instead of `extend` + +We considered making "extending name lookup" the default and overriding that +default with the `external` keyword. The argument for this option rested on it +being more convenient to express conditional internal implementation, and wasn't +seen as attractive once that feature was removed. In the discussion, we +generally preferred marking additional places to consult in name lookup since +that was something we expected readers of the code to want to specifically look +for. + +This option was proposed as the third option in the +[original #995 question](https://github.com/carbon-language/carbon-lang/issues/995#issue-1085174338), +and was considered in +[this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1006096692). + +### Use `mix` keyword for mixins + +We considered a variety of different syntax options for using a mixin for a type +in +[this comment on #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1315948818). +Many of them used the `mix` keyword, but we ultimately decided that `mix` was +redundant with saying `extend`, which we wanted to be included with all +constructs extending the type's API by another entity. + +### Allow more control over access to mixins + +There were three different aspects of mixins that we considered giving control +over access: + +- the inclusion of names exported by the mixin into the mixer class, +- the ability to cast between the mixin and the mixer class types, and +- the ability to use the name of the mixin given by the mixer class to access + members of the mixin. + +This was particularly relevant when we were considering separate declarations +for declaring the mixin member and the API extension +([1](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1315948818), +[2](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1317598076)). + +This approach had the problem that the common case for mixins was extending the +API, which would not have been the default. The only examples we had where the +mixer class would not want to include the names exported by the mixin were cases +where the mixin had nothing to export. This led to +[the position](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1325746398) +that the mixin would control which of its member would be included into the +mixer class' API -- that is the mixin would "inject" members rather than +"export" them and leave it up to the mixer class to import them. + +We had concerns that there might be name conflicts, but we thought those might +be handled by some other mechanism. This is being considered in +question-for-leads issue +[#2745: Name conflicts beyond inheritance](https://github.com/carbon-language/carbon-lang/issues/2745). + +We wanted mixin member names to behave consistently like other class member +names, and so default to public but can have a `private` modifier to make +private, following +[#665: `private` vs `public` _syntax_ strategy, as well as other visibility tools like `external`/`api`/etc.](https://github.com/carbon-language/carbon-lang/issues/665). +We decided to put the `private` keyword _between_ the `extend` keyword and the +member name for two reasons: + +- to make it easier to scan for all uses of `extend` in a class, and +- to make it clearer that the `private` access control only applies to the + member name, not what the `extend` controls. + +We did not see a use case for controlling the ability to cast between the mixin +and the mixer class types separately from being able to access the name the +mixin member of the mixin class. This was consistent with our desire to limit +declarations to a single access control specifier per declaration, see +[this update in #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1322892195). + +### List base class in class declaration + +By moving the base class into the class body, we accomplished three things: + +- made forward declarations shorter, +- made it possible to inherit from a type declared inside the body of the + class definition, and +- allowed greater consistency, all API extensions are now declarations in the + body of the class definition starting with `extend`. + +This was decided in +[this comment on #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1317598076). + +This left open the question of what keyword introducer to use in base class +declarations, since using `base` would cause an ambiguity with declaring a +member class that could be extended, as considered in +[this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1320614313). +Ultimately we avoided this problem by requiring base class declarations to +always begin with `extend`, not support any form of private or protected +inheritance (see +[this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1325746398)), +and not support any combination of an `extend` declaration with a member class +declaration.