Sfoglia il codice sorgente

Generic details 11: operator overloading (#1144)

Operators rewrite to calls of specific operator interface functions, so you overload an operator for a type by implementing an interface for it. There is a `like` operator for defining a set if implementations for supporting implicit conversions more conveniently.

Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
josh11b 4 anni fa
parent
commit
db66a4350e

+ 12 - 8
docs/design/classes.md

@@ -56,13 +56,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
         -   [Friends](#friends)
         -   [Friends](#friends)
         -   [Test friendship](#test-friendship)
         -   [Test friendship](#test-friendship)
         -   [Access control for construction](#access-control-for-construction)
         -   [Access control for construction](#access-control-for-construction)
+    -   [Operator overloading](#operator-overloading)
 -   [Future work](#future-work)
 -   [Future work](#future-work)
     -   [Struct literal shortcut](#struct-literal-shortcut)
     -   [Struct literal shortcut](#struct-literal-shortcut)
     -   [Optional named parameters](#optional-named-parameters)
     -   [Optional named parameters](#optional-named-parameters)
         -   [Field defaults for struct types](#field-defaults-for-struct-types)
         -   [Field defaults for struct types](#field-defaults-for-struct-types)
         -   [Destructuring in pattern matching](#destructuring-in-pattern-matching)
         -   [Destructuring in pattern matching](#destructuring-in-pattern-matching)
         -   [Discussion](#discussion)
         -   [Discussion](#discussion)
-    -   [Operator overloading](#operator-overloading)
     -   [Inheritance](#inheritance-1)
     -   [Inheritance](#inheritance-1)
         -   [Destructors](#destructors)
         -   [Destructors](#destructors)
         -   [C++ abstract base classes interoperating with object-safe interfaces](#c-abstract-base-classes-interoperating-with-object-safe-interfaces)
         -   [C++ abstract base classes interoperating with object-safe interfaces](#c-abstract-base-classes-interoperating-with-object-safe-interfaces)
@@ -1548,6 +1548,17 @@ if it has access to (write) all of its fields.
 even when it only has public fields. This will be resolved in question-for-leads
 even when it only has public fields. This will be resolved in question-for-leads
 issue [#803](https://github.com/carbon-language/carbon-lang/issues/803).
 issue [#803](https://github.com/carbon-language/carbon-lang/issues/803).
 
 
+### Operator overloading
+
+Developers may define how standard Carbon operators, such as `+` and `/`, apply
+to custom types by implementing the
+[interface](generics/terminology.md#interface) that corresponds to that operator
+for the types of the operands. See the
+["operator overloading" section](generics/details.md#operator-overloading) of
+the [generics design](generics/overview.md). The specific interface used for a
+given operator may be found in the
+[expressions design](/docs/design/expressions/README.md).
+
 ## Future work
 ## Future work
 
 
 This includes features that need to be designed, questions to answer, and a
 This includes features that need to be designed, questions to answer, and a
@@ -1636,13 +1647,6 @@ Some discussion on this topic has occurred in:
     [2](https://docs.google.com/document/d/1u6GORSkcgThMAiYKOqsgALcEviEtcghGb5TTVT-U-N0/edit)
     [2](https://docs.google.com/document/d/1u6GORSkcgThMAiYKOqsgALcEviEtcghGb5TTVT-U-N0/edit)
 -   ["match" in syntax choices doc](https://docs.google.com/document/d/1iuytei37LPg_tEd6xe-O6P_bpN7TIbEjNtFMLYW2Nno/edit#heading=h.y566d16ivoy2)
 -   ["match" in syntax choices doc](https://docs.google.com/document/d/1iuytei37LPg_tEd6xe-O6P_bpN7TIbEjNtFMLYW2Nno/edit#heading=h.y566d16ivoy2)
 
 
-### Operator overloading
-
-This includes destructors, copy and move operations, as well as other Carbon
-operators such as `+` and `/`. We expect types to implement these operations by
-implementing corresponding interfaces, see
-[the generics overview](generics/overview.md).
-
 ### Inheritance
 ### Inheritance
 
 
 #### Destructors
 #### Destructors

+ 347 - 6
docs/design/generics/details.md

@@ -95,6 +95,9 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 -   [Interface members with definitions](#interface-members-with-definitions)
 -   [Interface members with definitions](#interface-members-with-definitions)
     -   [Interface defaults](#interface-defaults)
     -   [Interface defaults](#interface-defaults)
     -   [`final` members](#final-members)
     -   [`final` members](#final-members)
+-   [Operator overloading](#operator-overloading)
+    -   [Binary operators](#binary-operators)
+    -   [`like` operator for implicit conversions](#like-operator-for-implicit-conversions)
 -   [Future work](#future-work)
 -   [Future work](#future-work)
     -   [Dynamic types](#dynamic-types)
     -   [Dynamic types](#dynamic-types)
         -   [Runtime type parameters](#runtime-type-parameters)
         -   [Runtime type parameters](#runtime-type-parameters)
@@ -102,7 +105,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     -   [Abstract return types](#abstract-return-types)
     -   [Abstract return types](#abstract-return-types)
     -   [Evolution](#evolution)
     -   [Evolution](#evolution)
     -   [Testing](#testing)
     -   [Testing](#testing)
-    -   [Operator overloading](#operator-overloading)
     -   [Impls with state](#impls-with-state)
     -   [Impls with state](#impls-with-state)
     -   [Generic associated types and higher-ranked types](#generic-associated-types-and-higher-ranked-types)
     -   [Generic associated types and higher-ranked types](#generic-associated-types-and-higher-ranked-types)
         -   [Generic associated types](#generic-associated-types)
         -   [Generic associated types](#generic-associated-types)
@@ -4440,6 +4442,349 @@ There are a few reasons for this feature:
 
 
 Note that this applies to associated entities, not interface parameters.
 Note that this applies to associated entities, not interface parameters.
 
 
+## Operator overloading
+
+Operations are overloaded for a type by implementing an interface specific to
+that interface for that type. For example, types implement the `Negatable`
+interface to overload the unary `-` operator:
+
+```
+// Unary `-`.
+interface Negatable {
+  let Result:! Type = Self;
+  fn Negate[me: Self]() -> Result;
+}
+```
+
+Expressions using operators are rewritten into calls to these interface methods.
+For example, `-x` would be rewritten to `x.(Negatable.Negate)()`.
+
+The interfaces and rewrites used for a given operator may be found in the
+[expressions design](/docs/design/expressions/README.md).
+[Question-for-leads issue #1058](https://github.com/carbon-language/carbon-lang/issues/1058)
+defines the naming scheme for these interfaces.
+
+### Binary operators
+
+Binary operators will have an interface that is
+[parameterized](#parameterized-interfaces) based on the second operand. For
+example, to say a type may be converted to another type using an `as`
+expression, implement the
+[`As` interface](/docs/design/expressions/as_expressions.md#extensibility):
+
+```
+interface As(Dest:! Type) {
+  fn Convert[me: Self]() -> Dest;
+}
+```
+
+The expression `x as U` is rewritten to `x.(As(U).Convert)()`. Note that the
+parameterization of the interface means it can be implemented multiple times to
+support multiple operand types.
+
+Unlike `as`, for most binary operators the interface's argument will be the
+_type_ of the right-hand operand instead of its _value_. Consider an interface
+for a binary operator like `*`:
+
+```
+// Binary `*`.
+interface MultipliableWith(U:! Type) {
+  let Result:! Type = Self;
+  fn Multiply[me: Self](other: U) -> Result;
+}
+```
+
+A use of binary `*` in source code will be rewritten to use this interface:
+
+```
+var left: Meters = ...;
+var right: f64 = ...;
+var result: auto = left * right;
+// Equivalent to:
+var equivalent: left.(MultipliableWith(f64).Result)
+    = left.(MultipliableWith(f64).Multiply)(right);
+```
+
+Note that if the types of the two operands are different, then swapping the
+order of the operands will result in a different implementation being selected.
+It is up to the developer to make those consistent when that is appropriate. The
+standard library will provide [adapters](#adapting-types) for defining the
+second implementation from the first, as in:
+
+```
+interface ComparableWith(RHS:! Type) {
+  fn Compare[me: Self](right: RHS) -> CompareResult;
+}
+
+adapter ReverseComparison
+    (T:! Type, U:! ComparableWith(RHS)) for T {
+  impl as ComparableWith(U) {
+    fn Compare[me: Self](right: RHS) -> CompareResult {
+      return ReverseCompareResult(right.Compare(me));
+    }
+  }
+}
+
+external impl SongByTitle as ComparableWith(SongTitle);
+external impl SongTitle as ComparableWith(SongByTitle)
+    = ReverseComparison(SongTitle, SongByTitle);
+```
+
+In some cases the reverse operation may not be defined. For example, a library
+might support subtracting a vector from a point, but not the other way around.
+
+Further note that even if the reverse implementation exists,
+[the impl prioritization rule](#prioritization-rule) might not pick it. For
+example, if we have two types that support comparison with anything implementing
+an interface that the other implements:
+
+```
+interface IntLike {
+  fn AsInt[me: Self]() -> i64;
+}
+
+class EvenInt { ... }
+external impl EvenInt as IntLike;
+external impl EvenInt as ComparableWith(EvenInt);
+// Allow `EvenInt` to be compared with anything that
+// implements `IntLike`, in either order.
+external impl [T:! IntLike] EvenInt as ComparableWith(T);
+external impl [T:! IntLike] T as ComparableWith(EvenInt);
+
+class PositiveInt { ... }
+external impl PositiveInt as IntLike;
+external impl PositiveInt as ComparableWith(PositiveInt);
+// Allow `PositiveInt` to be compared with anything that
+// implements `IntLike`, in either order.
+external impl [T:! IntLike] PositiveInt as ComparableWith(T);
+external impl [T:! IntLike] T as ComparableWith(PositiveInt);
+```
+
+Then it will favor selecting the implementation based on the type of the
+left-hand operand:
+
+```
+var even: EvenInt = ...;
+var positive: PositiveInt = ...;
+// Uses `EvenInt as ComparableWith(T)` impl
+if (even < positive) { ... }
+// Uses `PositiveInt as ComparableWith(T)` impl
+if (positive > even) { ... }
+```
+
+### `like` operator for implicit conversions
+
+Because the type of the operands is directly used to select the implementation
+to use, there are no automatic implicit conversions, unlike with function or
+method calls. Given both a method and an interface implementation for
+multiplying by a value of type `f64`:
+
+```
+class Meters {
+  fn Scale[me: Self](s: f64) -> Self;
+}
+// "Implementation One"
+external impl Meters as MultipliableWith(f64)
+    where .Result = Meters {
+  fn Multiply[me: Self](other: f64) -> Result {
+    return me.Scale(other);
+  }
+}
+```
+
+the method will work with any argument that can be implicitly converted to `f64`
+but the operator overload will only work with values that have the specific type
+of `f64`:
+
+```
+var height: Meters = ...;
+var scale: f32 = 1.25;
+// ✅ Allowed: `scale` implicitly converted
+//             from `f32` to `f64`.
+var allowed: Meters = height.Scale(scale);
+// ❌ Illegal: `Meters` doesn't implement
+//             `MultipliableWith(f32)`.
+var illegal: Meters = height * scale;
+```
+
+The workaround is to define a parameterized implementation that performs the
+conversion. The implementation is for types that implement the
+[`ImplicitAs` interface](/docs/design/expressions/implicit_conversions.md#extensibility).
+
+```
+// "Implementation Two"
+external impl [T:! ImplicitAs(f64)]
+    Meters as MultipliableWith(T) where .Result = Meters {
+  fn Multiply[me: Self](other: T) -> Result {
+    // Carbon will implicitly convert `other` from type
+    // `T` to `f64` to perform this call.
+    return me.(Meters.(MultipliableWith(f64).Multiply))(other);
+  }
+}
+// ✅ Allowed: uses `Meters as MultipliableWith(T)` impl
+//             with `T == f32` since `f32 is ImplicitAs(f64)`.
+var now_allowed: Meters = height * scale;
+```
+
+Observe that the [prioritization rule](#prioritization-rule) will still prefer
+the unparameterized impl when there is an exact match.
+
+To reduce the boilerplate needed to support these implicit conversions when
+defining operator overloads, Carbon has the `like` operator. This operator can
+only be used in the type or type-of-type part of an `impl` declaration, as part
+of a forward declaration or definition, in a place of a type.
+
+```
+// Notice `f64` has been replaced by `like f64`
+// compared to "implementation one" above.
+external impl Meters as MultipliableWith(like f64)
+    where .Result = Meters {
+  fn Multiply[me: Self](other: f64) -> Result {
+    return me.Scale(other);
+  }
+}
+```
+
+This `impl` definition actually defines two implementations. The first is the
+same as this definition with `like f64` replaced by `f64`, giving something
+equivalent to "implementation one". The second implementation replaces the
+`like f64` with a parameter that ranges over types that can be implicitly
+converted to `f64`, equivalent to "implementation two".
+
+In general, each `like` adds one additional impl. There is always the impl with
+all of the `like` expressions replaced by their arguments with the definition
+supplied in the source code. In addition, for each `like` expression, there is
+an impl with it replaced by a new parameter. These additional impls will
+delegate to the main impl, which will trigger implicit conversions according to
+[Carbon's ordinary implicit conversion rules](/docs/design/expressions/implicit_conversions.md).
+In this example, there are two uses of `like`, producing three implementations
+
+```
+external impl like Meters as MultipliableWith(like f64)
+    where .Result = Meters {
+  fn Multiply[me: Self](other: f64) -> Result {
+    return me.Scale(other);
+  }
+}
+```
+
+is equivalent to "implementation one", "implementation two", and:
+
+```
+external impl [T:! ImplicitAs(Meters)]
+    T as MultipliableWith(f64) where .Result = Meters {
+  fn Multiply[me: Self](other: f64) -> Result {
+    // Will implicitly convert `me` to `Meters` in order to
+    // match the signature of this `Multiply` method.
+    return me.(Meters.(MultipliableWith(f64).Multiply))(other);
+  }
+}
+```
+
+`like` may be used in forward declarations in a way analogous to impl
+definitions.
+
+```
+external impl like Meters as MultipliableWith(like f64)
+    where .Result = Meters;
+}
+```
+
+is equivalent to:
+
+```
+// All `like`s removed. Same as the declaration part of
+// "implementation one", without the body of the definition.
+external impl Meters as MultipliableWith(f64)
+    where .Result = Meters;
+
+// First `like` replaced with a wildcard.
+external impl [T:! ImplicitAs(Meters)]
+    T as MultipliableWith(f64) where .Result = Meters;
+
+// Second `like` replaced with a wildcard. Same as the
+// declaration part of "implementation two", without the
+// body of the definition.
+external impl [T:! ImplicitAs(f64)]
+    Meters as MultipliableWith(T) where .Result = Meters;
+```
+
+In addition, the generated impl definition for a `like` is implicitly injected
+at the end of the (unique) source file in which the impl is first declared. That
+is, it is injected in the API file if the impl is declared in an API file, and
+in the sole impl file declaring the impl otherwise. This means an `impl`
+declaration using `like` in an API file also makes the parameterized definition
+
+If one `impl` declaration uses `like`, other declarations must use `like` in the
+same way to match.
+
+The `like` operator may be nested, as in:
+
+```
+external impl like Vector(like String) as Printable;
+```
+
+Which will generate implementations with declarations:
+
+```
+external impl Vector(String) as Printable;
+external impl [T:! ImplicitAs(Vector(String))] T as Printable;
+external impl [T:! ImplicitAs(String)] Vector(T) as Printable;
+```
+
+The generated implementations must be legal or the `like` is illegal. For
+example, it must be legal to define those impls in this library by the
+[orphan rule](#orphan-rule). In addition, the generated `impl` definitions must
+only require implicit conversions that are guaranteed to exist. For example,
+there existing an implicit conversion from `T` to `String` does not imply that
+there is one from `Vector(T)` to `Vector(String)`, so the following use of
+`like` is illegal:
+
+```
+// ❌ Illegal: Can't convert a value with type
+//             `Vector(T:! ImplicitAs(String))`
+//             to `Vector(String)` for `me`
+//             parameter of `Printable.Print`.
+external impl Vector(like String) as Printable;
+```
+
+Since the additional implementation definitions are generated eagerly, these
+errors will be reported in the file with the first declaration.
+
+The argument to `like` must either not mention any type parameters, or those
+parameters must be able to be determined due to being repeated outside of the
+`like` expression.
+
+```
+// ✅ Allowed: no parameters
+external impl like Meters as Printable;
+
+// ❌ Illegal: No other way to determine `T`
+external impl [T:! IntLike] like T as Printable;
+
+// ❌ Illegal: `T` being used in a `where` clause
+//             is insufficient.
+external impl [T:! IntLike] like T
+    as MultipliableWith(i64) where .Result = T;
+
+// ❌ Illegal: `like` can't be used in a `where`
+//             clause.
+external impl Meters as MultipliableWith(f64)
+    where .Result = like Meters;
+
+// ✅ Allowed: `T` can be determined by another
+//             part of the query.
+external impl [T:! IntLike] like T
+    as MultipliableWith(T) where .Result = T;
+external impl [T:! IntLike] T
+    as MultipliableWith(like T) where .Result = T;
+
+// ✅ Allowed: Only one `like` used at a time, so this
+//             is equivalent to the above two examples.
+external impl [T:! IntLike] like T
+    as MultipliableWith(like T) where .Result = T;
+```
+
 ## Future work
 ## Future work
 
 
 ### Dynamic types
 ### Dynamic types
@@ -4489,11 +4834,6 @@ supported and made safe.
 The idea is that you would write tests alongside an interface that validate the
 The idea is that you would write tests alongside an interface that validate the
 expected behavior of any type implementing that interface.
 expected behavior of any type implementing that interface.
 
 
-### Operator overloading
-
-We will need a story for defining how an operation is overloaded for a type by
-implementing an interface for that type.
-
 ### Impls with state
 ### Impls with state
 
 
 A feature we might consider where an `impl` itself can have state.
 A feature we might consider where an `impl` itself can have state.
@@ -4570,3 +4910,4 @@ be included in the declaration as well.
 -   [#983: Generic details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983)
 -   [#983: Generic details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983)
 -   [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990)
 -   [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990)
 -   [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013)
 -   [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013)
+-   [#1144: Generic details 11: operator overloading](https://github.com/carbon-language/carbon-lang/pull/1144)

+ 19 - 2
docs/design/generics/overview.md

@@ -35,6 +35,7 @@ pointers to other design documents that dive deeper into individual topics.
         -   [Parameterized interfaces](#parameterized-interfaces)
         -   [Parameterized interfaces](#parameterized-interfaces)
     -   [Constraints](#constraints)
     -   [Constraints](#constraints)
     -   [Parameterized impls](#parameterized-impls)
     -   [Parameterized impls](#parameterized-impls)
+    -   [Operator overloading](#operator-overloading)
 -   [Future work](#future-work)
 -   [Future work](#future-work)
 -   [References](#references)
 -   [References](#references)
 
 
@@ -627,14 +628,30 @@ library defining some name from its type structure. If a library defines
 multiple implementations with the same type structure, they must be listed in
 multiple implementations with the same type structure, they must be listed in
 priority order in a prioritization block.
 priority order in a prioritization block.
 
 
+### Operator overloading
+
+To overload an operator, implement the corresponding interface from the standard
+library. For example, to define how the unary `-` operator behaves for a type,
+implement the `Negatable` interface for that type. The interfaces and rewrites
+used for a given operator may be found in the
+[expressions design](/docs/design/expressions/README.md).
+
+As a convenience, there is a shorcut for defining an implementation that
+supports any type implicitly convertible to a specified type, using `like`:
+
+```
+// Support multiplying values of type `Distance` with
+// values of type `f64` or any type implicitly
+// convertible to `f64`.
+external impl Distance as MultipliableWith(like f64) ...
+```
+
 ## Future work
 ## Future work
 
 
 -   Support functions should have a way to accept types that types that vary at
 -   Support functions should have a way to accept types that types that vary at
     runtime.
     runtime.
 -   You should have the ability to mark entities as `upcoming` or `deprecated`
 -   You should have the ability to mark entities as `upcoming` or `deprecated`
     to support evolution.
     to support evolution.
--   Types should be able to define overloads for operators by implementing
-    standard interfaces.
 -   There should be a way to provide default implementations of methods in
 -   There should be a way to provide default implementations of methods in
     interfaces and other ways to reuse code across implementations.
     interfaces and other ways to reuse code across implementations.
 -   There should be a way to define generic associated and higher-ranked/kinded
 -   There should be a way to define generic associated and higher-ranked/kinded

+ 1 - 0
docs/design/lexical_conventions/words.md

@@ -61,6 +61,7 @@ The following words are interpreted as keywords:
 -   `is`
 -   `is`
 -   `let`
 -   `let`
 -   `library`
 -   `library`
+-   `like`
 -   `match`
 -   `match`
 -   `namespace`
 -   `namespace`
 -   `not`
 -   `not`

+ 157 - 0
proposals/p1144.md

@@ -0,0 +1,157 @@
+# Generic details 11: operator overloading
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/1144)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Problem](#problem)
+-   [Background](#background)
+-   [Proposal](#proposal)
+-   [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
+-   [Alternatives considered](#alternatives-considered)
+    -   [Weak impls instead of adapters for reverse implementations](#weak-impls-instead-of-adapters-for-reverse-implementations)
+    -   [Default impls instead of adapters for reverse implementations](#default-impls-instead-of-adapters-for-reverse-implementations)
+    -   [Allow an impl declaration with `like` to match one without](#allow-an-impl-declaration-with-like-to-match-one-without)
+    -   [Where are the impl definitions from `like` generated?](#where-are-the-impl-definitions-from-like-generated)
+    -   [Support marking interfaces or their members as `external`](#support-marking-interfaces-or-their-members-as-external)
+
+<!-- tocstop -->
+
+## Problem
+
+C++ supports
+[operator overloading](https://en.wikipedia.org/wiki/Operator_overloading), and
+we would like Carbon to as well. This proposal is about the general problem, not
+the specifics application to any particular operator.
+
+This proposal does not attempt to define a mechanism by which we can ensure that
+`a < b` has the same value as `b > a`.
+
+## Background
+
+The generics feature is the
+[single static open extension mechanism](/docs/project/principles/static_open_extension.md)
+in Carbon, and so will be what we use operator overloading. We have already
+started specifying the ability to extend or customize the behavior of operators
+by implementing interfaces, as in these proposals:
+
+-   [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820)
+-   [#845: as expressions](https://github.com/carbon-language/carbon-lang/pull/845)
+-   [#911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911)
+-   [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083)
+
+Proposal
+[#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702)
+specified
+[using interfaces for overloading the comparison operators](p0702.md#overloading),
+but did not pin down specifically what those interfaces are.
+
+## Proposal
+
+This proposal adds an
+["Operator overloading" section](/docs/design/generics/details.md#operator-overloading)
+to the [detailed design of generics](/docs/design/generics/details.md).
+
+## Rationale based on Carbon's goals
+
+This proposal advances Carbon's goals:
+
+-   [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write),
+    by making common constructs more concise, and allowing the syntax to more
+    closely mirror notation used math or the application domain.
+-   [Software and language evolution](/docs/project/goals.md#software-and-language-evolution),
+    since this allows standard types to implement operators using the same
+    mechanisms that user types do, this allows changes between what is built-in
+    versus provided in a library without user-visible impact.
+
+## Alternatives considered
+
+The current proposal requires the user to define a reverse implementation, and
+recommends using an adapter to do that more conveniently. We also considered
+approaches that would provide the reverse implementation more automatically.
+
+### Weak impls instead of adapters for reverse implementations
+
+We proposed
+[weak impls](https://github.com/carbon-language/carbon-lang/pull/1027) as a way
+of defining blanket impls for the reverse impl that did not introduce
+[cycles](/docs/design/generics/details.md#acyclic-rule). We rejected that
+approach due to giving the reverse implementation the wrong priority. This meant
+that there were many situations where `a < b` and `b > a` would give different
+answers.
+
+### Default impls instead of adapters for reverse implementations
+
+We then proposed
+[default impls](https://github.com/carbon-language/carbon-lang/pull/1034) as a
+way to define reverse implementations. These were rejected because they had a
+lot of overlap with blanket impls, making it difficult to describe when to use
+one over the other, and because they introduced a lot of complexity without
+fully solving the priority problem. Most of the complexity was from the criteria
+for determining whether the default implementation would be used. As
+[noted](/docs/design/generics/details.md#binary-operators), the current proposal
+still has some priority issues, but this way the relevant impls are visible in
+the source which will hopefully make it clearer why it happens.
+
+The capability provided by default impls -- the ability to conveniently give
+implementations of other interfaces -- may prove useful enough that we would
+reconsider this decision in the future.
+
+### Allow an impl declaration with `like` to match one without
+
+We considered allowing an impl declared with `like` to match the equivalent
+impls without `like`. The main concern was there would not be a canonical form
+without `like`, particularly of how the newly introduced parameter would be
+written. We thought we might say that the `like` declaration, since it omits a
+spelling of the parameter, is allowed to match any spelling of the parameter.
+However, there would still be a question of whether to use a deduced parameter,
+as in `[T:! ImplicitAs(i64)] Vector(T)` or not as in
+`Vector(T:! ImplicitAs(i64))`. We also considered the canonical form of
+`Vector(_:! ImplicitAs(i64))` without naming the parameter. In the end, we
+decided to start with a restrictive approach with the knowledge that we could
+change once we gained experience.
+
+The main use case for allowing declarations in a different form, which may
+motivate changes in the future, is to prioritize the different implementations
+generated by the `like` shortcut separately in `match_first` blocks.
+
+This was discussed in
+[open discussion on 2022-03-24](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#).
+
+### Where are the impl definitions from `like` generated?
+
+We considered whether the additional impl definitions would be generated with
+the first declaration of an impl using `like` or with its definition. We
+ultimately decided on the former approach for two reasons:
+
+-   The generated impl definitions are parameterized even if the explicit
+    definition is not, and parameterized impl definitions may need to be in the
+    API file to allow separate compilation.
+-   This will make the code doing implicit conversions visible to callers,
+    allowing it to be inlined, matching how the caller does implicit conversions
+    for method calls.
+
+This was discussed in
+[the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/962059164014739526).
+
+### Support marking interfaces or their members as `external`
+
+We
+[discussed on 2022-03-28](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.sk06n1ggoa3o)
+the idea that operator interfaces might be marked `external`. This would either
+mean that types would only be able to implement them using `external impl` or
+that even if they were implemented internally, they would not add the names of
+the interface members to the type. Alternatively, individual members might be
+marked `external` to indicate that their names are not added to implementing
+types, which might also be useful for making changes to an interface in a
+compatible way.
+
+We were not sure if this feature was needed, so we left this as future work.