From 55e9cb4b55d3a226ca2cd8c895c79230e925461c Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Aug 2014 09:36:03 -0700 Subject: [PATCH 1/8] Initial version of "`Compose` trait" RFC --- active/0000-compose-trait.md | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 active/0000-compose-trait.md diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md new file mode 100644 index 00000000000..15cac0d2292 --- /dev/null +++ b/active/0000-compose-trait.md @@ -0,0 +1,62 @@ +- Start Date: 2014-08-17 +- RFC PR #: (leave this empty) +- Rust Issue #: https://github.com/rust-lang/rust/issues/16541 + +# Summary + +A `Compose` trait is added with a single function `compose` which desugars to +the `++` operator. The `Add` implementation for `String` is replaced by one +for `Compose`. + +# Motivation + +As @huonw mentions in https://github.com/rust-lang/rust/issues/14482, mathematical +convention is that addition is commutative. The stdlib already follows mathematical +convention in some cases (for example, `Zero` requires `Add` and is expected to +be the additive identity; ditto for `One` and `Mul`). + +It is an (often unstated) assumption in many algorithms that `+` is a commutative +operator. Violating this assumption in the stdlib forces programmers to memorize +that `+` means something different in rust than it does everywhere else, and also +risks encouraging abuse of operator overloading. + +There is a postponed proposal regarding having unit tests for Traits which enforce +invariants; commutativity of `+` is an natural one and it would be bad if it was +unenforcable because standard library was violating it. + +# Detailed design + +Currently everything in the stdlib which implements `Add` implements `add` as a +commutative operator, except for strings. Therefore I propose: +- Introduce a `Compose` trait with a `compose` function that sugars to the `++` +operator. +- Implement this on `String` for concatenation. This replaces `Add` for `String`. +- Add "must be commutative" to the documentation for `Add`. +- Add "must be associative" to the documentation for `Compose`. + +For those interested in algebraic names, this makes `++` into a semigroup operator. +Users who want an abelian group can then use `Add+Zero+Neg` (or `Add+Zero+Sub`, +this ambiguity should probably be addressed in a later RFC related to fixing the +numeric traits once we have associated items); users who want an arbitrary group +can use `Mul+One+Div`; users who want a monoid can use `Compose+Default`, etc. + +This way nobody is surprised by generic code which sometimes does the Wrong Thing, +but we avoid having a Haskell-like scenario where every category-theoretic object +is supported (with corresponding mental load). We just have a few binary operators +each with simple conventions. + +# Drawbacks + +Code which uses `+` to add strings will need to use `++` instead. + +# Alternatives + +Leave `+` as is. + +# Unresolved questions + +`Compose` should also be used for function compositions, at least for single-argument +functions `T->T`. How would this interact with our current/future coherence rules? + +Where else should `Compose` be used? + From 565a58fd6dde9615b3ce6a7e67b3010edf2fa55f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Aug 2014 14:46:14 -0700 Subject: [PATCH 2/8] Add trait def for Compose --- active/0000-compose-trait.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index 15cac0d2292..3ba21b74cab 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -34,6 +34,19 @@ operator. - Add "must be commutative" to the documentation for `Add`. - Add "must be associative" to the documentation for `Compose`. +The signature of `compose` is exactly the same as that for `add` and the other +binary operators: + +````rust +pub trait Compose { + /// The method for the `++` operator + fn compose(&self, rhs: &RHS) -> Result; +} +```` +and will be updated alongside the other binary-operation traits as the trait system +is revamped. (For example, adding `ComposeAssign` for in in-place `++=` or making +`Result` an associated item.) + For those interested in algebraic names, this makes `++` into a semigroup operator. Users who want an abelian group can then use `Add+Zero+Neg` (or `Add+Zero+Sub`, this ambiguity should probably be addressed in a later RFC related to fixing the From 7efd853a301ac0c8de33650dfbbcc7359a642dbc Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Aug 2014 14:49:03 -0700 Subject: [PATCH 3/8] Add note to impl ++ for Vec, DList, etc --- active/0000-compose-trait.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index 3ba21b74cab..92112882fb2 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -31,6 +31,8 @@ commutative operator, except for strings. Therefore I propose: - Introduce a `Compose` trait with a `compose` function that sugars to the `++` operator. - Implement this on `String` for concatenation. This replaces `Add` for `String`. +- Implement this on `Bitv`, `DList`, `Vec` and any other "linear" collections + where concatenation makes sense. - Add "must be commutative" to the documentation for `Add`. - Add "must be associative" to the documentation for `Compose`. From f01367c15d81746a04cdfb62fcf236851c6be1fe Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Aug 2014 15:40:59 -0700 Subject: [PATCH 4/8] Add `Compose` impl for `Path` and `Iterator` (thanks @olivier-renaud) --- active/0000-compose-trait.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index 92112882fb2..358d7c61f25 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -33,6 +33,8 @@ operator. - Implement this on `String` for concatenation. This replaces `Add` for `String`. - Implement this on `Bitv`, `DList`, `Vec` and any other "linear" collections where concatenation makes sense. +- Implement this on `Path` as a synonym for `join` +- Implement this on `Iterator` as a synonym for `chain` - Add "must be commutative" to the documentation for `Add`. - Add "must be associative" to the documentation for `Compose`. From dd0347a4cd9dd1eba75084f4164ecd6a636233fb Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 18 Aug 2014 14:32:34 -0700 Subject: [PATCH 5/8] Update `Vec` and `Bitv` to "all collections" --- active/0000-compose-trait.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index 358d7c61f25..6fa32fc9a6d 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -31,8 +31,9 @@ commutative operator, except for strings. Therefore I propose: - Introduce a `Compose` trait with a `compose` function that sugars to the `++` operator. - Implement this on `String` for concatenation. This replaces `Add` for `String`. -- Implement this on `Bitv`, `DList`, `Vec` and any other "linear" collections - where concatenation makes sense. +- Implement this for every collection, using the same semantics as their `Extendable` + implementations. (`Vec`s and `Bitv`s would be concatenated, sets and maps would + be unioned, etc.) - Implement this on `Path` as a synonym for `join` - Implement this on `Iterator` as a synonym for `chain` - Add "must be commutative" to the documentation for `Add`. From 8de7aefa2f7d59dde93c51e9b30916dcf8aa8921 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 19 Aug 2014 08:29:43 -0700 Subject: [PATCH 6/8] Rename Compose/compose to Combine/combine --- active/0000-compose-trait.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index 6fa32fc9a6d..b1e578cf0ee 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -4,9 +4,9 @@ # Summary -A `Compose` trait is added with a single function `compose` which desugars to +A `Combine` trait is added with a single function `combine` which desugars to the `++` operator. The `Add` implementation for `String` is replaced by one -for `Compose`. +for `Combine`. # Motivation @@ -28,7 +28,7 @@ unenforcable because standard library was violating it. Currently everything in the stdlib which implements `Add` implements `add` as a commutative operator, except for strings. Therefore I propose: -- Introduce a `Compose` trait with a `compose` function that sugars to the `++` +- Introduce a `Combine` trait with a `combino` function that sugars to the `++` operator. - Implement this on `String` for concatenation. This replaces `Add` for `String`. - Implement this for every collection, using the same semantics as their `Extendable` @@ -37,26 +37,26 @@ operator. - Implement this on `Path` as a synonym for `join` - Implement this on `Iterator` as a synonym for `chain` - Add "must be commutative" to the documentation for `Add`. -- Add "must be associative" to the documentation for `Compose`. +- Add "must be associative" to the documentation for `Combine`. -The signature of `compose` is exactly the same as that for `add` and the other +The signature of `combine` is exactly the same as that for `add` and the other binary operators: ````rust -pub trait Compose { +pub trait Combine { /// The method for the `++` operator - fn compose(&self, rhs: &RHS) -> Result; + fn combine(&self, rhs: &RHS) -> Result; } ```` and will be updated alongside the other binary-operation traits as the trait system -is revamped. (For example, adding `ComposeAssign` for in in-place `++=` or making +is revamped. (For example, adding `CombineAssign` for in in-place `++=` or making `Result` an associated item.) For those interested in algebraic names, this makes `++` into a semigroup operator. Users who want an abelian group can then use `Add+Zero+Neg` (or `Add+Zero+Sub`, this ambiguity should probably be addressed in a later RFC related to fixing the numeric traits once we have associated items); users who want an arbitrary group -can use `Mul+One+Div`; users who want a monoid can use `Compose+Default`, etc. +can use `Mul+One+Div`; users who want a monoid can use `Combine+Default`, etc. This way nobody is surprised by generic code which sometimes does the Wrong Thing, but we avoid having a Haskell-like scenario where every category-theoretic object @@ -73,8 +73,8 @@ Leave `+` as is. # Unresolved questions -`Compose` should also be used for function compositions, at least for single-argument +`Combine` should also be used for function compositions, at least for single-argument functions `T->T`. How would this interact with our current/future coherence rules? -Where else should `Compose` be used? +Where else should `Combine` be used? From bb7070f59616f7ef8ea709b8c9c756b65a2721ad Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 19 Aug 2014 10:31:21 -0700 Subject: [PATCH 7/8] Declare precedence of ++ (between bitwise and comparisons) --- active/0000-compose-trait.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index b1e578cf0ee..cbd37f0540e 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -4,7 +4,7 @@ # Summary -A `Combine` trait is added with a single function `combine` which desugars to +A `Combine` trait is added with a single function `combine` which sugars to the `++` operator. The `Add` implementation for `String` is replaced by one for `Combine`. @@ -29,7 +29,7 @@ unenforcable because standard library was violating it. Currently everything in the stdlib which implements `Add` implements `add` as a commutative operator, except for strings. Therefore I propose: - Introduce a `Combine` trait with a `combino` function that sugars to the `++` -operator. +operator. The precedence of `++` is between the bitwise operators and the comparison operators. - Implement this on `String` for concatenation. This replaces `Add` for `String`. - Implement this for every collection, using the same semantics as their `Extendable` implementations. (`Vec`s and `Bitv`s would be concatenated, sets and maps would From 1132cc01c2bd3f452581f699e8c12f4644655320 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 13 Sep 2014 09:28:10 -0500 Subject: [PATCH 8/8] Update for @huonw's comments -- in particular, remove `Iterator` impl --- active/0000-compose-trait.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/active/0000-compose-trait.md b/active/0000-compose-trait.md index cbd37f0540e..f1d4109a198 100644 --- a/active/0000-compose-trait.md +++ b/active/0000-compose-trait.md @@ -17,8 +17,8 @@ be the additive identity; ditto for `One` and `Mul`). It is an (often unstated) assumption in many algorithms that `+` is a commutative operator. Violating this assumption in the stdlib forces programmers to memorize -that `+` means something different in rust than it does everywhere else, and also -risks encouraging abuse of operator overloading. +that `+` means something different in rust than it does in common language, and +also risks encouraging abuse of operator overloading. There is a postponed proposal regarding having unit tests for Traits which enforce invariants; commutativity of `+` is an natural one and it would be bad if it was @@ -28,14 +28,13 @@ unenforcable because standard library was violating it. Currently everything in the stdlib which implements `Add` implements `add` as a commutative operator, except for strings. Therefore I propose: -- Introduce a `Combine` trait with a `combino` function that sugars to the `++` +- Introduce a `Combine` trait with a `combine` function that sugars to the `++` operator. The precedence of `++` is between the bitwise operators and the comparison operators. - Implement this on `String` for concatenation. This replaces `Add` for `String`. - Implement this for every collection, using the same semantics as their `Extendable` implementations. (`Vec`s and `Bitv`s would be concatenated, sets and maps would be unioned, etc.) - Implement this on `Path` as a synonym for `join` -- Implement this on `Iterator` as a synonym for `chain` - Add "must be commutative" to the documentation for `Add`. - Add "must be associative" to the documentation for `Combine`. @@ -49,7 +48,7 @@ pub trait Combine { } ```` and will be updated alongside the other binary-operation traits as the trait system -is revamped. (For example, adding `CombineAssign` for in in-place `++=` or making +is revamped. (For example, adding `CombineAssign` for in-place `++=` or making `Result` an associated item.) For those interested in algebraic names, this makes `++` into a semigroup operator.