mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Rework operator interfaces (#1178)
Add concrete design for interfaces for comparison. Rename interfaces for arithmetic following current thinking in #1058. Update rules for mixed-type comparisons for data classes following #710. Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Chandler Carruth
parent
f42ca044cb
commit
4a8ca9cd0f
@@ -182,77 +182,75 @@ following family of interfaces:
|
||||
|
||||
```
|
||||
// Unary `-`.
|
||||
interface Negatable {
|
||||
interface Negate {
|
||||
let Result:! Type = Self;
|
||||
fn Negate[me: Self]() -> Result;
|
||||
fn Op[me: Self]() -> Result;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `+`.
|
||||
interface AddableWith(U:! Type) {
|
||||
interface AddWith(U:! Type) {
|
||||
let Result:! Type = Self;
|
||||
fn Add[me: Self](other: U) -> Result;
|
||||
fn Op[me: Self](other: U) -> Result;
|
||||
}
|
||||
constraint Addable {
|
||||
extends AddableWith(Self) where .Result = Self;
|
||||
constraint Add {
|
||||
extends AddWith(Self) where .Result = Self;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `-`.
|
||||
interface SubtractableWith(U:! Type) {
|
||||
interface SubWith(U:! Type) {
|
||||
let Result:! Type = Self;
|
||||
fn Subtract[me: Self](other: U) -> Result;
|
||||
fn Op[me: Self](other: U) -> Result;
|
||||
}
|
||||
constraint Subtractable {
|
||||
extends SubtractableWith(Self) where .Result = Self;
|
||||
constraint Sub {
|
||||
extends SubWith(Self) where .Result = Self;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `*`.
|
||||
interface MultipliableWith(U:! Type) {
|
||||
interface MulWith(U:! Type) {
|
||||
let Result:! Type = Self;
|
||||
fn Multiply[me: Self](other: U) -> Result;
|
||||
fn Op[me: Self](other: U) -> Result;
|
||||
}
|
||||
constraint Multipliable {
|
||||
extends MultipliableWith(Self) where .Result = Self;
|
||||
constraint Mul {
|
||||
extends MulWith(Self) where .Result = Self;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `/`.
|
||||
interface DividableWith(U:! Type) {
|
||||
interface DivWith(U:! Type) {
|
||||
let Result:! Type = Self;
|
||||
fn Divide[me: Self](other: U) -> Result;
|
||||
fn Op[me: Self](other: U) -> Result;
|
||||
}
|
||||
constraint Dividable {
|
||||
extends DividableWith(Self) where .Result = Self;
|
||||
constraint Div {
|
||||
extends DivWith(Self) where .Result = Self;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `%`.
|
||||
interface ModuloWith(U:! Type) {
|
||||
interface ModWith(U:! Type) {
|
||||
let Result:! Type = Self;
|
||||
fn Mod[me: Self](other: U) -> Result;
|
||||
fn Op[me: Self](other: U) -> Result;
|
||||
}
|
||||
constraint Modulo {
|
||||
extends ModuloWith(Self) where .Result = Self;
|
||||
constraint Mod {
|
||||
extends ModWith(Self) where .Result = Self;
|
||||
}
|
||||
```
|
||||
|
||||
Given `x: T` and `y: U`:
|
||||
|
||||
- The expression `-x` is rewritten to `x.(Negatable.Negate)()`.
|
||||
- The expression `x + y` is rewritten to `x.(AddableWith(U).Add)(y)`.
|
||||
- The expression `x - y` is rewritten to
|
||||
`x.(SubtractableWith(U).Subtract)(y)`.
|
||||
- The expression `x * y` is rewritten to
|
||||
`x.(MultipliableWith(U).Multiply)(y)`.
|
||||
- The expression `x / y` is rewritten to `x.(DividableWith(U).Divide)(y)`.
|
||||
- The expression `x % y` is rewritten to `x.(ModuloWith(U).Mod)(y)`.
|
||||
- The expression `-x` is rewritten to `x.(Negate.Op)()`.
|
||||
- The expression `x + y` is rewritten to `x.(AddWith(U).Op)(y)`.
|
||||
- The expression `x - y` is rewritten to `x.(SubWith(U).Op)(y)`.
|
||||
- The expression `x * y` is rewritten to `x.(MulWith(U).Op)(y)`.
|
||||
- The expression `x / y` is rewritten to `x.(DivWith(U).Op)(y)`.
|
||||
- The expression `x % y` is rewritten to `x.(ModWith(U).Op)(y)`.
|
||||
|
||||
Implementations of these interfaces are provided for built-in types as necessary
|
||||
to give the semantics described above.
|
||||
@@ -278,4 +276,6 @@ to give the semantics described above.
|
||||
## References
|
||||
|
||||
- Proposal
|
||||
[#1083: arithmetic](https://github.com/carbon-language/carbon-lang/pull/1083).
|
||||
[#1083: Arithmetic](https://github.com/carbon-language/carbon-lang/pull/1083)
|
||||
- Proposal
|
||||
[#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178)
|
||||
|
||||
@@ -102,7 +102,7 @@ unordered with respect to binary arithmetic, bitwise operators, and unary `not`.
|
||||
|
||||
```
|
||||
// OK
|
||||
var x: i32* as Comparable;
|
||||
var x: i32* as Eq;
|
||||
// OK, `x as (U*)` not `(x as U)*`.
|
||||
var y: auto = x as U*;
|
||||
|
||||
|
||||
@@ -17,7 +17,11 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Built-in comparisons and implicit conversions](#built-in-comparisons-and-implicit-conversions)
|
||||
- [Consistency with implicit conversions](#consistency-with-implicit-conversions)
|
||||
- [Comparisons with constants](#comparisons-with-constants)
|
||||
- [Overloading](#overloading)
|
||||
- [Extensibility](#extensibility)
|
||||
- [Equality](#equality)
|
||||
- [Ordering](#ordering)
|
||||
- [Compatibility of equality and ordering](#compatibility-of-equality-and-ordering)
|
||||
- [Custom result types](#custom-result-types)
|
||||
- [Default implementations for basic types](#default-implementations-for-basic-types)
|
||||
- [Open questions](#open-questions)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
@@ -43,6 +47,15 @@ Comparison operators all return a `bool`; they evaluate to `true` when the
|
||||
indicated comparison is true. All comparison operators are infix binary
|
||||
operators.
|
||||
|
||||
These operators have predefined meanings for some of Carbon's
|
||||
[built-in types](#built-in-comparisons-and-implicit-conversions), as well as for
|
||||
simple ["data" types](#default-implementations-for-basic-types) like structs and
|
||||
tuples.
|
||||
|
||||
User-defined types can define the meaning of these operations by
|
||||
[implementing an interface](#extensibility) provided as part of the Carbon
|
||||
standard library.
|
||||
|
||||
## Details
|
||||
|
||||
### Precedence
|
||||
@@ -222,29 +235,245 @@ literal that cannot be represented in `i32`. Such comparisons would always be
|
||||
tautological. This decision should be revisited if it proves problematic in
|
||||
practice, for example in templated code where the literal is sometimes in range.
|
||||
|
||||
### Overloading
|
||||
### Extensibility
|
||||
|
||||
Separate interfaces will be provided to permit overloading equality and
|
||||
relational comparisons. The exact design of those interfaces is left to a future
|
||||
proposal. As non-binding design guidance for such a proposal:
|
||||
User-defined types can extend the behavior of the comparison operators by
|
||||
implementing interfaces. In this section, various properties are specified that
|
||||
such implementations "should" satisfy. These properties are not enforced in
|
||||
general, but the standard library might detect violations of some of them in
|
||||
some circumstances. These properties may be assumed by generic code, resulting
|
||||
in unexpected behavior if they are violated.
|
||||
|
||||
- The interface for equality comparisons should primarily provide the ability
|
||||
to override the behavior of `==`. The `!=` operator can optionally also be
|
||||
overridden, with a default implementation that returns `not (a == b)`. This
|
||||
conversation was marked as resolved by chandlerc Show conversation
|
||||
Overriding `!=` separately from `==` is expected to be used to support
|
||||
floating-point NaN comparisons and for C++ interoperability.
|
||||
#### Equality
|
||||
|
||||
- The interface for relational comparisons should primarily provide the
|
||||
ability to specify a three-way comparison operator. The individual
|
||||
relational comparison operators can optionally be overridden separately,
|
||||
with a default implementation in terms of the three-way comparison operator.
|
||||
This facility is expected to be used primarily to support C++
|
||||
interoperability.
|
||||
Comparison operators can be provided for user-defined types by implementing the
|
||||
`EqWith` and `OrderedWith` interfaces.
|
||||
|
||||
- Overloaded comparison operators may wish to produce a type other than
|
||||
`bool`, for uses such as a vector comparison producing a vector of `bool`
|
||||
values. We should decide whether we wish to support such uses.
|
||||
The `EqWith` interface is used to define the semantics of the `==` and `!=`
|
||||
operators for a given pair of types:
|
||||
|
||||
```
|
||||
interface EqWith(U:! Type) {
|
||||
fn Equal[me: Self](u: U) -> bool;
|
||||
default fn NotEqual[me: Self](u: U) -> bool {
|
||||
return not (me == u);
|
||||
}
|
||||
}
|
||||
constraint Eq {
|
||||
extends EqWith(Self);
|
||||
}
|
||||
```
|
||||
|
||||
Given `x: T` and `y: U`:
|
||||
|
||||
- The expression `x == y` calls `x.(EqWith(U).Equal)(y)`.
|
||||
- The expression `x != y` calls `x.(EqWith(U).NotEqual)(y)`.
|
||||
|
||||
```
|
||||
class Path {
|
||||
private var drive: String;
|
||||
private var path: String;
|
||||
private fn CanonicalPath[me: Self]() -> String;
|
||||
|
||||
external impl as Eq {
|
||||
fn Equal[me: Self](other: Self) -> bool {
|
||||
return (me.drive, me.CanonicalPath()) ==
|
||||
(other.drive, other.CanonicalPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `EqWith` overload is selected without considering possible implicit
|
||||
conversions. To permit implicit conversions in the operands of an `==` overload,
|
||||
the
|
||||
[`like` operator](/docs/design/generics/details.md#like-operator-for-implicit-conversions)
|
||||
can be used:
|
||||
|
||||
```
|
||||
class MyInt {
|
||||
var value: i32;
|
||||
fn Value[me: Self]() -> i32 { return me.value; }
|
||||
}
|
||||
external impl i32 as ImplicitAs(MyInt);
|
||||
external impl like MyInt as EqWith(like MyInt) {
|
||||
fn Equal[me: Self](other: Self) -> bool {
|
||||
return me.Value() == other.Value();
|
||||
}
|
||||
}
|
||||
fn CompareBothWays(a: MyInt, b: i32, c: MyInt) -> bool {
|
||||
// OK, calls above implementation three times.
|
||||
return a == a and a != b and b == c;
|
||||
}
|
||||
```
|
||||
|
||||
The behavior of `NotEqual` can be overridden separately from the behavior of
|
||||
`Equal` to support cases like floating-point NaN values, where two values can
|
||||
compare neither equal nor not-equal, and thus both functions would return
|
||||
`false`. However, an implementation of `EqWith` should _not_ allow both `Equal`
|
||||
and `NotEqual` to return `true` for the same pair of values. Additionally, these
|
||||
operations should have no observable side-effects.
|
||||
|
||||
```
|
||||
external impl like MyFloat as EqWith(like MyFloat) {
|
||||
fn Equal[me: MyFloat](other: MyFloat) -> bool {
|
||||
if (me.IsNaN() or other.IsNaN()) {
|
||||
return false;
|
||||
}
|
||||
return me.Representation() == other.Representation();
|
||||
}
|
||||
fn NotEqual[me: MyFloat](other: MyFloat) -> bool {
|
||||
if (me.IsNaN() or other.IsNaN()) {
|
||||
return false;
|
||||
}
|
||||
return me.Representation() != other.Representation();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Heterogeneous comparisons must be defined both ways around:
|
||||
|
||||
```
|
||||
external impl like MyInt as EqWith(like MyFloat);
|
||||
external impl like MyFloat as EqWith(like MyInt);
|
||||
```
|
||||
|
||||
**TODO:** Add an adapter to the standard library to make it easy to define the
|
||||
reverse comparison.
|
||||
|
||||
#### Ordering
|
||||
|
||||
The `OrderedWith` interface is used to define the semantics of the `<`, `<=`,
|
||||
`>`, and `>=` operators for a given pair of types.
|
||||
|
||||
```
|
||||
choice Ordering {
|
||||
Less,
|
||||
Equivalent,
|
||||
Greater,
|
||||
Incomparable
|
||||
}
|
||||
interface OrderedWith(U:! Type) {
|
||||
fn Compare[me: Self](u: U) -> Ordering;
|
||||
default fn Less[me: Self](u: U) -> bool {
|
||||
return me.Compare(u) == Ordering.Less;
|
||||
}
|
||||
default fn LessOrEquivalent[me: Self](u: U) -> bool {
|
||||
let c: Ordering = me.Compare(u);
|
||||
return c == Ordering.Less or c == Ordering.Equivalent;
|
||||
}
|
||||
default fn Greater[me: Self](u: U) -> bool {
|
||||
return me.Compare(u) == Ordering.Greater;
|
||||
}
|
||||
default fn GreaterOrEquivalent[me: Self](u: U) -> bool {
|
||||
let c: Ordering = me.Compare(u);
|
||||
return c == Ordering.Greater or c == Ordering.Equivalent;
|
||||
}
|
||||
}
|
||||
constraint Ordered {
|
||||
extends OrderedWith(Self);
|
||||
}
|
||||
|
||||
// Ordering.Less < Ordering.Equivalent < Ordering.Greater.
|
||||
// Ordering.Incomparable is incomparable with all three.
|
||||
external impl Ordering as Ordered;
|
||||
```
|
||||
|
||||
**TODO:** Revise the above when we have a concrete design for enumerated types.
|
||||
|
||||
Given `x: T` and `y: U`:
|
||||
|
||||
- The expression `x < y` calls `x.(OrderedWith(U).Less)(y)`.
|
||||
- The expression `x <= y` calls `x.(OrderedWith(U).LessOrEquivalent)(y)`.
|
||||
- The expression `x > y` calls `x.(OrderedWith(U).Greater)(y)`.
|
||||
- The expression `x >= y` calls `x.(OrderedWith(U).GreaterOrEquivalent)(y)`.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
class MyWidget {
|
||||
var width: i32;
|
||||
var height: i32;
|
||||
|
||||
fn Size[me: Self]() -> i32 { return me.width * me.height; }
|
||||
|
||||
// Widgets are normally ordered by size.
|
||||
external impl as Ordered {
|
||||
fn Compare[me: Self](other: Self) -> Ordering {
|
||||
return me.Size().(Ordered.Compare)(other.Size());
|
||||
}
|
||||
}
|
||||
}
|
||||
fn F(a: MyWidget, b: MyWidget) -> bool {
|
||||
return a <= b;
|
||||
}
|
||||
```
|
||||
|
||||
As for `EqWith`, the
|
||||
[`like` operator](/docs/design/generics/details.md#like-operator-for-implicit-conversions)
|
||||
can be used to permit implicit conversions when invoking a comparison, and
|
||||
heterogeneous comparisons must be defined both ways around:
|
||||
|
||||
```
|
||||
fn ReverseOrdering(o: Ordering) -> Ordering {
|
||||
return Ordering.Equivalent.(Ordered.Compare)(o);
|
||||
}
|
||||
external impl like MyInt as OrderedWith(like MyFloat);
|
||||
external impl like MyFloat as OrderedWith(like MyInt) {
|
||||
fn Compare[me: Self](other: Self) -> Ordering {
|
||||
return Reverse(other.(OrderedWith(Self).Compare)(me));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The default implementations of `Less`, `LessOrEquivalent`, `Greater`, and
|
||||
`GreaterOrEquivalent` can be overridden if a more efficient version can be
|
||||
implemented. The behaviors of such overrides should follow those of the above
|
||||
default implementations, and the members of an `OrderedWith` implementation
|
||||
should have no observable side-effects.
|
||||
|
||||
`OrderedWith` implementations should be _transitive_. That is, given `V:! Type`,
|
||||
`U:! OrderedWith(V)`, `T:! OrderedWith(U) & OrderedWith(V)`, `a: T`, `b: U`,
|
||||
`c: V`, then:
|
||||
|
||||
- If `a <= b` and `b <= c` then `a <= c`, and moreover if either `a < b` or
|
||||
`b < c` then `a < c`.
|
||||
- If `a >= b` and `b >= c` then `a >= c`, and moreover if either `a > b` or
|
||||
`b > c` then `a > c`.
|
||||
- If `a` and `b` are equivalent, then `a.Compare(c) == b.Compare(c)`.
|
||||
Similarly, if `b` and `c` are equivalent, then
|
||||
`a.Compare(b) == a.Compare(c)`.
|
||||
|
||||
`OrderedWith` implementations should also be _consistent under reversal_. That
|
||||
is, given types `T` and `U` where `T is OrderedWith(U)` and
|
||||
`U is OrderedWith(T)`, and values `a: T` and `b: U`:
|
||||
|
||||
- If `a.(OrderedWith.Compare)(b)` is `Ordering.Greater`, then
|
||||
`b.(OrderedWith.Compare)(a)` is `Ordering.Less`, and the other way around.
|
||||
- Otherwise, `a.(OrderedWith.Compare)(b)` returns the same value as
|
||||
`b.(OrderedWith.Compare)(a)`.
|
||||
|
||||
There is no expectation that an `Ordered` implementation be a total order, a
|
||||
weak order, or a partial order, and in particular the implementation for
|
||||
floating-point types is none of these because NaN values do not compare less
|
||||
than or equivalent to themselves.
|
||||
|
||||
**TODO:** The standard library should provide a way to specify that an ordering
|
||||
is a weak, partial, or total ordering, and a way to request such an ordering in
|
||||
a generic.
|
||||
|
||||
#### Compatibility of equality and ordering
|
||||
|
||||
There is no requirement that a pair of types that implements `OrderedWith` also
|
||||
implements `EqWith`. If a pair of types does implement both, however, the
|
||||
equality relation provided by `x.(EqWith.Equal)(y)` should be a refinement of
|
||||
the equivalence relation provided by
|
||||
`x.(OrderedWith.Compare)(y) == Ordering.Equivalent`.
|
||||
|
||||
#### Custom result types
|
||||
|
||||
**TODO:** Support a lower-level extensibility mechanism that allows a result
|
||||
type other than `bool`.
|
||||
|
||||
### Default implementations for basic types
|
||||
|
||||
@@ -253,12 +482,27 @@ relational comparisons are also defined for all "data" types:
|
||||
|
||||
- [Tuples](../tuples.md)
|
||||
- [Struct types](../classes.md#struct-types)
|
||||
- [Classes implementing an interface that identifies them as data classes.](../classes.md#interfaces-implemented-for-data-classes)
|
||||
- [Classes implementing an interface that identifies them as data classes](../classes.md#interfaces-implemented-for-data-classes)
|
||||
|
||||
Relational comparisons for these types provide a lexicographical ordering. In
|
||||
each case, the comparison is only available if it is supported by all element
|
||||
types.
|
||||
|
||||
Because implicit conversions between data classes can reorder fields, the
|
||||
implementations for data classes do not permit implicit conversions on their
|
||||
arguments in general. Instead:
|
||||
|
||||
- Equality comparisons are permitted between any two data classes that have
|
||||
the same _unordered set_ of field names, if each corresponding pair of
|
||||
fields has an `EqWith` implementation. Fields are compared in the order they
|
||||
appear in the left-hand operand.
|
||||
- Relational comparisons are permitted between any two data classes that have
|
||||
the same _ordered sequence_ of field names, if each corresponding pair of
|
||||
fields has an `OrderedWith` implementation. Fields are compared in order.
|
||||
|
||||
Comparisons between tuples permit implicit conversions for either operand, but
|
||||
not both.
|
||||
|
||||
## Open questions
|
||||
|
||||
The `bool` type should be treated as a choice type, and so should support
|
||||
@@ -272,10 +516,13 @@ in general. That decision is left to a future proposal.
|
||||
- [Convert operands like C++](/proposals/p0702.md#convert-operands-like-c)
|
||||
- [Provide a three-way comparison operator](/proposals/p0702.md#provide-a-three-way-comparison-operator)
|
||||
- [Allow comparisons as the operand of `not`](/proposals/p0702.md#allow-comparisons-as-the-operand-of-not)
|
||||
- [Rename `OrderedWith` to `ComparableWith`](/proposals/p1178.md#use-comparablewith-instead-of-orderedwith)
|
||||
|
||||
## References
|
||||
|
||||
- Proposal
|
||||
[#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702)
|
||||
- Proposal
|
||||
[#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178)
|
||||
- Issue
|
||||
[#710: Default comparison for data classes](https://github.com/carbon-language/carbon-lang/issues/710)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Rework operator interfaces
|
||||
|
||||
<!--
|
||||
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/1178
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Use `ComparableWith` instead of `OrderedWith`](#use-comparablewith-instead-of-orderedwith)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Problem
|
||||
|
||||
Our operator interface names need to be updated to match the decision in
|
||||
[#1058](https://github.com/carbon-language/carbon-lang/issues/1058). Further, we
|
||||
are missing a description of the interfaces used to overload comparison
|
||||
operators, and the rules are not up to date with the decision in
|
||||
[#710](https://github.com/carbon-language/carbon-lang/issues/710).
|
||||
|
||||
## Background
|
||||
|
||||
See the two leads issues for background and discussion of options.
|
||||
|
||||
## Proposal
|
||||
|
||||
See changes to the design.
|
||||
|
||||
## Details
|
||||
|
||||
Beyond establishing names for interfaces, this proposal also establishes:
|
||||
|
||||
- We will have high-level interfaces for equality and relational comparison.
|
||||
The equality interface provides both `==` and `!=`. The relational
|
||||
comparison interface provides all of `<`, `<=`, `>`, and `>=`.
|
||||
- Following the convention established for arithmetic operators, we provide
|
||||
both a heterogeneous comparison interface and a homogeneous constraint. For
|
||||
example, `T is EqWith(T)` is equivalent to `T is Eq`.
|
||||
- The high-level interfaces always return `bool`.
|
||||
- The high-level interfaces have expected semantics associated with them.
|
||||
|
||||
It is intended that we also provide low-level interfaces, to directly control
|
||||
individual operators and to allow a result type other than `bool`. These are not
|
||||
included in this proposal, as it's not yet clear how they should be specified,
|
||||
and it's more important to get the high-level interfaces decided at this point.
|
||||
|
||||
## Rationale
|
||||
|
||||
- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem)
|
||||
- High-level semantics allow tools to reason about the intended meaning of
|
||||
Carbon code. For example, a tool could statically or dynamically
|
||||
determine that an implementation of `Ordered` doesn't satisfy the
|
||||
expected rules and produce a warning.
|
||||
- [Performance-critical software](/docs/project/goals.md#performance-critical-software)
|
||||
- We expect `==` and ordering to be customized separately, in order to
|
||||
avoid cases where a suboptimal `==` is constructed in terms of an
|
||||
ordering. See
|
||||
[C++ committee paper P1190R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1190r0.html)
|
||||
for details on the problem.
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
|
||||
- Combining all comparison operators of the same kind -- equality or
|
||||
relational -- into a single interface makes it both easier to implement
|
||||
them and easier to write a generic constraint for them. This approach is
|
||||
also expected to be easy to teach, with the low-level interfaces only
|
||||
explained to a more advanced audience.
|
||||
- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms)
|
||||
- While there are rules for the comparison interfaces, violating those
|
||||
rules does not result in immediate unbounded undefined behavior.
|
||||
However, implementations should still attempt to detect violations of
|
||||
these rules and report them where that is feasible.
|
||||
- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)
|
||||
- The intent to provide a low-level interface for individual operators is
|
||||
directly motivated by the desire to provide strong interoperability with
|
||||
operators defined in C++. While this functionality is not part of this
|
||||
proposal, it's expected to follow once the interactions with generics
|
||||
are worked out.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Use `ComparableWith` instead of `OrderedWith`
|
||||
|
||||
We could use the term "comparable" for relational comparisons instead of
|
||||
"ordered". There is existing practice for both: for example, Rust and Haskell
|
||||
use `Ord`, and Swift uses `Comparable`.
|
||||
|
||||
The main argument for using "ordered" instead of "comparable" is that `==` and
|
||||
`!=` are also a form of comparison but aren't part of `OrderedWith`, and the
|
||||
word "ordered" distinguishes relational comparison from equality comparison.
|
||||
Reference in New Issue
Block a user