-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Combine
trait for string and collection concatenation
#203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
55e9cb4
565a58f
7efd853
f01367c
dd0347a
8de7aef
bb7070f
1132cc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
- Start Date: 2014-08-17 | ||
- RFC PR #: (leave this empty) | ||
- Rust Issue #: https://github.com/rust-lang/rust/issues/16541 | ||
|
||
# Summary | ||
|
||
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`. | ||
|
||
# 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 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 | ||
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 `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` | ||
- Add "must be commutative" to the documentation for `Add`. | ||
- Add "must be associative" to the documentation for `Combine`. | ||
|
||
The signature of `combine` is exactly the same as that for `add` and the other | ||
binary operators: | ||
|
||
````rust | ||
pub trait Combine<RHS,Result> { | ||
/// The method for the `++` operator | ||
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 `CombineAssign` for 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 `Combine+Default`, etc. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if I want to write a generic method that works on all groups? According to this I would write fn f<T: Mul+One+Div>(... Naturally this algorithm also works for all abelian groups, but how does the compiler know that? The biggest argument for this RFC seems to be that abstract algorithms can make certain assumptions, but does this scheme even support abstract algorithms? |
||
|
||
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 | ||
|
||
`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 `Combine` be used? | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add
trait is an interface, not an implementation. You couldn't assume whether it's a commutative or not.Traits specify the interfaces not implementations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does
Eq
fit into this claim?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A trait summarises common behaviour, e.g. things that implement
Eq
are expected to have a sensible implementation of equality, things that implementAdd
are expected to have a sensible definition of addition.Functions can operate assuming that these traits have sensible implementations or else they will return unexpected results, e.g.
HashMap
will not give expected results if a type has a badEq
implementation.The only thing a non-
unsafe
stdlib function can't do is be memory unsafe with a violation of these sensible properties, since that would allow memory-unsafety in safe code.This sort of contract (i.e. not enforcable in the signature of a trait) is quite common and is the whole point of traits. It's not just that certain types have a method with a certain signature, but also that that method acts in a particular way.