-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Combine
trait for string and collection concatenation
#203
Conversation
I think the move away from |
@apoelstra IMO this could very well have gone up on discourse first. (I saw your comment on rust-lang/rust#16541 that mentioned potentially doing that.) My opinion is that draft RFC's can go through rounds of improvement on discourse first. But then again, perhaps you are more eager to see a quick yay/nay rather than going through discourse-based bike-shedding before coming here. As for the operator itself: Have you considered that going forward we probably want to encourage use of |
I think we should go with @pnkfelix, yes I do believe we need |
👍 I've suggested this before as well. @apoelstra What would be the signature of the |
According to the wikipedia article on Monoids, especially the part on "Monoids in computer science", CS people tend to utilize monoids with "accumulating" or "folding" operations. And string concatenations and function compositions are "accumulating" operations. We have other uses for the name The problem is, does "Accumulate" imply the involvement of more than two elements? Sorry that I am on my phone and cannot edit the previous post. |
@japaric, or |
I'm in favor of either |
@japaric, I think And |
@japaric @pnkfelix Yes, SOP for discourse is endless bikeshedding on such things as This RFC came out of several IRC discussions with people familiar with Rust and the problem space, and has already undergone revisions because of it. For example I wanted |
The RFC should mention using |
The current RFC only proposes |
Oh! FWIW the signature of Edit2: Thanks for the suggestion @bluss. Updated RFC. |
`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? |
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.
Compose
could be used on an Iterator
, as a synonym of the chain
method. It could also be used on GenericPath
, performing a join
.
I am very strongly against any requirement of commutativity on |
Andrew Poelstra [email protected] writes:
|
SiegeLord [email protected] writes:
|
@apoelstra, I think all collection types that implement Extendable are eligible for |
It depends exactly what we define commutativity to mean. For expression templates, the memory of |
+1 to the general idea. I like having the separation just for a intuitive scanning of code. |
Yes, what huonw said. One more issue I thought of today is types declared outside of |
@SiegeLord a fair point. |
@SiegeLord Yes, I would consider an operation to be commutative even if one of the commutations can't be written down. This throws a wrench in my dreams of having unit tests for commutativity, but those were already broken by the fact that you can define The main reason is that I consider "one of the commutations can't be written down" to be a bug in the current trait definitions, which would be fixed by multiple dispatch. The two type parameters to (most) binary operators should be treated on equal footing. |
I am strongly in favor of this RFC. Rust has, should absolutely be implementing the best traits defined in languages like Haskell that abstract massively popular patterns. |
@CloudiDust Good call on |
@liigo, we are not creating another operator, EDIT: Grammar. |
@liigo, also, just because something is widespread in mainstream languages, doesn't automatically mean it is right, or Rust should adopt it. Unconstrained |
@liigo I think a reason against However their |
@CloudiDust My last comment focused on "common languages", not ++ or +. (I'm not against ++) |
@liigo "common language" means ordinary English. In English, "addition" refers to a commutative operator. As for "you can abuse I am not proposing that we abuse |
@apoelstra Yes I see. thanks for you explanation. |
@liigo, I was just pointing out a possible reason that was against |
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 comment
The 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?
I'd find it a bit more natural to remove the
One example: trait Field {
fn zero() -> Self;
fn one() -> Self;
fn add(self, other: &Self) -> Self;
fn sub(self, other: &Self) -> Self;
fn neg(self) -> Self {
Field::zero().sub(&self)
}
fn mul(self, other: &Self) -> Self;
fn div(self, other: &Self) -> Self;
fn inv(self) -> Self {
Field::one().div(&self)
}
} As you can see, the functions take Then comes the compiler magic: Every type can implement at most one of these traits and the compiler knows, e.g., that every monoid is also a magma. I'm not yet sure if there's really a good story here. The biggest problem is that PS: |
Please god don't add group-theoretic categories to Rust's standard library. Monoid/Monad/Functor has caused untold educational pain in the Haskell world. If we have to have them, they should at least have decent names that relate to what they actually are. |
If operating overloading is intended to be abused for creating an arbitrary DSL, then it shouldn't be tied to traits. Traits without guarantees aren't useful, because it's not possible to write correct generic code with them. I think it should be tied to traits and adding better support for creating a DSL is a problem for language improvements to deal with. The ability to define custom operators or infix call sugar would alleviate the need to abuse the basic operators. The I don't think new operators should be added as special cases. Please just propose a general solution rather than adding more and more built-in operators. |
Don't worry, those really have nothing to do with what I've described above. |
I really don't think operator abuse is that clear cut. E.g. it might be useful to have a trait which adds an overload for So it seems obvious to me that it's better to divorse operator overloading from traits (i.e. do what @mahkoh suggested except perhaps with less jargon and compiler magic). That way we can have two traits, commutative |
@SiegeLord If "commutative multiplication" refers to a group operator, then addition can be used. If addition is already taken, presumably we are in a ring or something, and the appropriate way to approach this is to have traits I also don't think this sort of thing belongs in the standard library, but rather in some external "mathtypelib" which would have the full gamut of algebraic structures. All I'm trying to do with this RFC is make sure the stdlib doesn't discourage that by setting a bad example. |
@apoelstra That seems completely opposite of what you suggest in the RFC, however. If you want to defer the semantics to those mathy traits, then the operator overloading traits should have no semantics at all, and should not be used in generic code directly (i.e. you'd use your |
I think it's an awful workaround for the lack of better language features. Reusing the same few names or operators for different operations is poor API design. It should be possible to define custom operators if operators are going to be used as sugar for cases not meeting sane requirements for the mathematical traits. |
I don't disagree, but in the realm of syntax sugar where there is enormous pressure to pick very short names, this will be inevitable, even with custom operators. |
@SiegeLord Hmm, you're right, my "all I'm trying to do" comment is a bit disingenuous. I definitely think that On the other hand, I worry about the slide into Haskellism where we are intimidating new users with too much abstract algebra, which is why I don't advocate having My feeling is that commutativity and associativity are okay to enforce where appropriate, because I think that ordinary programmers are very familiar with these concepts. |
I think a sensible middle ground, which mostly tracks the current approach, might be:
This allows full syntactic flexibility for EDSLs while still retaining sanity for normal generic code. The main drawback is a readability hit: you wouldn't be able see the use of an operator in arbitrary code and assume that it has any particular types or laws; if it's generic code you would have to look at what trait bounds are being required, and for non-generic code you would need to be familiar with the type it's being used with. (Of course, community pressure can still be applied to discourage truly egregious abuses.) Tying the operator overloads directly to laws as @thestinger suggests is appealing, and if that's the road we eventually choose to go down, it wouldn't necessarily bother me. I think that's a perfectly sensible option as well. But not even GHC manages to entirely hold the line on this. They have an extension called |
Why should they be traits if they're not meant to be used as traits? It doesn't make much sense to me. If it's not doing the same thing, it shouldn't use the same name / operator. The debate is already over for |
It's a mechanism which already exists and works for the purpose, basically. The problem is that "the same thing" is too general a concept to be captured precisely in the type system (or at least in ours). An EDSL in which the |
The traits for overloading |
ping @huonw |
This RFC has been inactive for quite some time now, so I'm going to close this. It would be good to take a fresh look at this RFC with today's modern standard library, especially with respect to ops traits and how the ownership of their arguments have changed. I'd definitely believe there's still a place for a trait like this in the standard library, but it would be useful to start anew! |
Right now just specialize one element as that's the same size as a vector, but we can consider adding more later. Closes rust-lang#203
Rendered: https://github.com/apoelstra/rfcs/blob/compose-trait/active/0000-compose-trait.md
cc rust-lang/rust#16541