-
Notifications
You must be signed in to change notification settings - Fork 5
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
Operator overloading #72
Comments
What is the benefit of separating If we will support a "minimal complete definition" for traits (alike typeclasses in Haskell) (probably using an attribute), then my suggestion would be that the minimal complete definition for |
Sure, the |
I don't like idea of splitting of a = []
b = a # copy reference to list
a = a + [1] # call '__add__' to concat lists and make new one, then assign to 'a'
print(a, b) # [1], []. Original list wasn't mutated, new one was created and assigned to 'a'
a = []
b = a # copy reference to list
a += [1] # calls '__iadd__', which calls 'extend' (addRange)
print(a, b) # [1], [1]. Original list was mutated |
@thinker227 @jl0pd the mutable list scenario is exactly why Kotlin recognizes this potential ambiguity in the meaning of |
I mean that it's bad for language to have meaning for |
Isn't a lot of this duplicating .Net 7 generic math? For example, is it really a good idea to have both |
It is related, yes. But since we are providing our own BCL, why not ease it a bit, like removing the need for CRTP. For interop we can implement these interfaces. |
One downside of the Rust approach is that it assigns a semantic meaning to operators that may not be correct. For example, when you use I'm also not sure I agree that supporting arbitrary operator overloading is a bad thing. I agree it can easily be abused, but there are a lot of common math operators out there that our languages don't support out of the box, and it would be reasonable to allow overloading given that languages now support Unicode. |
Introduction
This issue talks about user-define operators on existing operator symbols (also known as operator overloading), not defining new, custom symbols as operators. This is the same mechanism as C# allows defining operators on user-defined types. As usual, we will go through a couple of designs and finally propose something for Fresh.
How C++ does it
Cppreference docs.
C++ does operator overloading in the simplest manner. Each individual operator can be overloaded for operand types - as long as one type is user-defined. For example, defining an addition for a 2D vector:
The existence of
+
does not imply+=
, which has to be implemented separately. The same is true between all relational operators, meaning that for a type that has total ordering, 6 operator overloads (<
,<=
,>
,>=
,==
,!=
) have to be implemented. This gives many opportunities to make a lot of repetition and typos in the many related operators. The existence of==
does not require the existence of!=
or vice versa, which does not respect the usual properties we expect from equuatable types (in case the user forgets to implement it).Note, that the situation has been somewhat eased with the spaceship operator for relational operators.
How C# does it
Official docs.
C# slightly improves the situation in 2 ways:
Compound operators are implemented using their binary equivalents. If you implement
+
, you automatically get+=
and there is no way to implement+=
yourself. This helps correctness and reduces the amount of boilerplate.Some operators are required to be overloaded in pairs, like
==
and!=
, or>
and<
. This improves in the sense that the operations are more predictable, but sadly this mechanism does not eliminate duplicate logic caused by this. This is due to the fact that we usually implementIEquatable
orIComparable
, and then let the IDE implement the operators based on that.How F# does it
Official docs.
F# seems to be a step back from C#. It essentially goes back to being the C++ way, making you define each operator yourself. It requires no operators to be defined in pairs/groups and implements no operators for you, not even the compound ones.
This isn't necessarily a step back in every aspect, as the user has more control over each operator, but I'd argue the value of keeping the properties of some of the operators and reducing duplication can be more valuable in most cases.
How Rust does it
Rust by example docs.
Rust decided to define a trait for each overloadable operator in std::ops. The compiler recognizes these trait implementations and allows the operator syntax for the implementors. The vector example from C++ rewritten in Rust:
The existence of
+
does not assume the existence of any other operator and does not provide+=
.I believe this trait-way has four major advantages:
Proposal for Fresh
See the traits issue for a full(er) context.
I believe we should follow the footsteps of Rust here again. It's a very clean and beautiful design, opening up more possibilities than the other designs. What I propose here is not exact, it's more like the motivation and idea to why we should go with this way.
We could define traits for all atomic operations:
This would allow the user to have the most control and have very fine-grained over everything. This would also be used by the compiler to recognize that the given type can be used with the given operator syntax.
We could also define higher level constructs that allow the user to define higher level mathematical constructs, like fields. Example (please note that I'm by no means a mathematician):
We could even define a trait that defines all 6 relational operators based on a single compare-like function (for types with total ordering), eliminating the need for something like a spaceship operator.
Since traits can be used as generic constraints, we would get things like generic math essentially for free.
Why not custom operator symbols?
I strongly believe that custom operator symbols are a mistake in all cases. They completely diverge from natural, standard mathematical notation and create incompatible DSLs. I believe providing infix function call syntax should cover most cases while being more readable than the custom option.
The text was updated successfully, but these errors were encountered: