-
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
Custom operators #818
Comments
It would make sense to title this issue "custom operators" or "user-defined operators" or something of that nature instead of "operator overloading". |
Is this being considered at all? |
@cameron-martin Could you provide example when you need custom operator and simple function couldn't help you? I am against custom operators because it is complex. |
The main problem with custom operators, which is quickly experienced if you've written any haskell is that the symbols included in the operator identifier does not usually inform the meaning of what the operator does, unless it is picked very judiciously. Standard operators used in mathematics are so often used that everyone knows their meaning instantly. A function which has its identifier written in English conveys the meaning of what it does much more clearly. Instead of adding custom operators, another beneficial idea would be to allow infix calls to functions like so: a `dot` b to convey scalar product of two vectors a and b. Which is converted to a.dot( b ) or, depending on the function: dot( a, b ) This also has the benefit of being similar to another language (haskell). |
I have a strong dislike for arbitrary user-defined operators. Essentially, you end up with each library providing an entirely different language. People quickly abuse this kind of stuff because it "seems nice at first", but it gets a mess for the library user. Parsing it is pretty much impossible too. It is worth noting that Rust already got operator overloading, which covers the majority of cases.
... and gals. |
As an example of what sort of excess can occur with user-defined operators, look at Haskell's Lens library. It's a fantastically useful library with an complex and opaque sub-language of special operators in it. Attempting to read Lens code without the operator reference open is a task only for the library's most experienced users. Rust already has complex syntax. I don't think more complexity is a good idea. The notion of infix operators is interesting, though I am not sure how much they get you over methods. My general instinct says to leave the syntactical complexity alone unless you have a good reason. |
I'll have to put in my vote against this idea. Building on @AndrewBrinker's comment, I feel Haskell has demonstrated exactly why this kind of feature usually doesn't belong in a language. The idiom of custom operator use in Haskell is a barrier to entry, hands down. I've often wondered to myself what a language with exactly the same features as Haskell (but minus the annoying and opaque syntax) would look like and if it wouldn't gain broader acceptance. If this kind of thing ever does find its way into Rust, it would have to be in the most sparing and sober way possible. |
Haskell went through so much pain, with the only advantage of having a mostly useless feature. In fact, it tend to be abused and makes everything a mess. The infix function is more sane, but I'm not sure if it is needed enough to be worth adding. |
I think custom syntax can help with mathematics, well crypto crates love Instead I'd look more towards the units crate which uses wrapper structs heavily. I think more important tools include:
|
@ticki @Centril While I don't like custom operators, I actually agree that infix invocation could be useful. I've even enjoyed using languages that also have prefix and suffix invocation (as with Haskell's |
Method syntax is infix invocation. |
I'm closing this RFC as most people seem to be aganist this idea. I admit that in general the usefulness of this feature is limited. I was writing parser combinators which is one of the few times custom operators could be useful. |
Just ran into this issue right now. I'm working on an application and I wanted to have a trait called Addressing some of the points made here:
|
To add another voice in favour and a use case that is not parser combinators, I have been involved in a number of ML-family projects that make extensive use of a "pipeline" operator Of course, its utility is greatly diminished when there is no good language support for binding all but one of a function's arguments (in the ML family, this is just achieved by ubiquitous currying and careful API design putting the argument that most likely wants to be "piped through" last), but I think it is a great example of a case where custom operators enable API design that palpably improves code quality. (Regarding the |
A point not mentioned here is that macro-by-example ( @blackhole89's concern about the |
In Haskell, custom infix operators are used everywhere. It's true, after a while there are so many it's hard to keep track of them mnemonically, but still, being able to add new operators is often incredibly useful. As long as it is clear what crate/module a custom operator comes from, readers can find the docs and figure out the semantics of any unusual, non-obvious custom operators. I strongly recommend reopening this RFC. |
Do you have a particular use case not mentioned above? If no new point is made, we would end up repeating the conversation above. |
I strongly recommend re-opening this RFC too. In specialized fields custom operators are well-understood within that community already. Replacing these with long names would reduce readability. Yes, newbies will have to learn this new "language" but that is true if you look at any highly specialized field. Language designers should not circumscribe this needlessly. If a particular user wants to give an "English name" to a particular operator nothing in Rust prevents them from doing so. But the opposite is not true currently. |
How do macros not solve your problem? Please read the discussion above. In particular:
|
How about having the custom operators be implemented using traits just like the operators in currently in Rust? For example: #[doc(alias = "|>")]
pub trait ForwardPipe<Rhs = Self> {
/// The resulting type after applying the `|>` operator.
type Output;
/// Performs the `|>` operation.
///
/// # Example
///
/// ```
/// fn print(message: &str) {
/// println!("{}", message);
/// }
///
/// // "Hello World" will be passed as a parameter to the print function
/// "Hello World" |> print;
///
/// // "Goodbye World" will be passed as a parameter to the print function
/// ForwardPipe::forward_pipe("Goodbye World");
/// ```
#[must_use]
fn forward_pipe(self, rhs: Rhs) -> Self::Output {
...
}
} Also, with this solution you can see the documentation on how to use the operator if the crate implemented it. So there there would be meaning to the operator than just a symbol. Named functions are great and easy to understand. They should be first consideration when having custom operators in a language but in some cases symbols may be even better. |
maybe we could implement something equivalent if #![binary_op(PartialEq::ne = <>)]
fn main() {
println("{}", 1 <> 2); // true
} |
With the ability to override existing operators, |
@EarthCitizen the impact of overriding existing operators only propagates to the resultant type and inference of the operator, but custom operators are way more complex than that, involving arbitrary precedence and associativity rules, unclear unary/binary-ness, etc., which can cause way more problems than just reading the documentation, especially for editors that are unable to resolve the custom operators (and yes, even rust-analyzer still constantly runs into macro resolution issues for me all the time). |
Original issue
There's some controversy over custom operators and them being overused in cases that don't really make sense and how they can be more confusing than helpful. I personally love them and think that they, when used wisely, can really make certain APIs much more appealing.
Rust allows you to overload a few basic operators using traits defined in the standard library. That's great but you're very limited to what you can actually do with them.
I'd like to point you to Swift here. They have solved this problem rather nicely.
You can implement operator functions and define some properties on them, like:
Here's how Rust could look defining a custom null coalescing operator:
I haven't given too much thought to the example above, it's probably suboptimal and doesn't play too well with the existing syntax. So it's just an example.
Basically, I'm interested in hearing what other people would think of more generic operator overloading functionality in Rust.
As I said before:
Fantastic work guys
The text was updated successfully, but these errors were encountered: