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
proposal: Go 2: user-defined equality and copying #30557
Comments
This seems like a subset of operator overloading; see #27605. It would probably be best if you added to that discussion instead of opening another proposal. |
The copying method is essentially the same that was proposed in #21604. |
Note that the current implementation relies on stack copying. If a type with a Note that the language currently does not treat any user defined methods as special. This proposed change would be the first case in which that is done. Even the I agree with the above that this is similar to #21604 (which was closed) and #27605. |
Thank you for bringing up other proposals #21604 and #27605 which I wasn't aware of. In fact, one of them is my old proposal. I am not sure whether my current proposal should be closed and I should join the discussion in those other proposals. While there are some overlapping areas, those proposals aren't the same as this proposal. #27605#27605 proposes operator overloading, but does not mention cloning/copying at all. The listed must overload operators are mathematical operators, such as +, -, *, /, etc. The equality operator (==) falls under a maybe. In my opinion, mathematical operator overloading isn't critical. You may have defined a type such as type Money int64 and uses it all over the place such as .. var sum Money = 10000
sum += 5000 When later you change type Money struct {
CurrencySymbol string
Amount int64
} The existing code with mathematical operators will just break and the compiler will refuse to compile. This allows you to conveniently fix the affected code. The same cannot be said with equality operators. Let's take the Money example above, and it is used all over the place such as .. if myMoney == herMoney {
//do something
} and later we changed money into something like.. type Money struct {
CurrencySymbol string
Amount int64
ExchangeRate int64 //exchange rate should be ignored in equality comparison.
//JPY 10000 should be equal to JPY 10000 regardless of their original purchase cost.
} The existing code will still compile, but it will introduce a subtle bug. Hunting down the affected code isn't trivial. Searching for every occurrence of == operator is ridiculous. Hence, in my opinion, #27605 is more about ergonomics, while this proposal is more about the language's flexibility when dealing with design myopia and refactoring. In addition, I don't agree with having too many special methods to overload these operators, as this will take away good method names from the users. #27605 lists way too many operators to overload. Hence, in this proposal, I limit those to only Equal and Clone. #27605 does not mention Copy/Clone (perhaps because there is no specific operator for copying?). #21604#21604 is my old proposal. I had forgotten all about it. #21604 discussed about constructor overloading, copy/clone override, and type inheritance. The copy/clone override part is essentially the same as this proposal's copy/clone suggestion. #21604 does not mention user-defined equality override, which I think is important. After an extra one year of experience using Go, #21604's constructor and inheritance proposals aren't probably that important. ClosingThe idea for this proposal came up from my own work. Having to search through every equality comparison and copying mechanism every time there is a change in data types that affect their equality/copying mechanism, is tedious and error prone. No IDE is smart enough to detect those. So, some sort of fundamental language support would be really nice. In Java and C#, you can just override the base object's Equal method and be done with it. In Go, there is no base object and no inheritance. In Go, if you have the foresight, in a new project, you may have provided the Equal and Clone methods and warned in the comment that the users should use the provided methods instead of the built-in operators. Still, this isn't elegant and somebody may not read the documentation. Now what if you haven't had the foresight and there are new requirements (eg. new features) to an existing codebase? Now you have to refactor, replacing those existing == and copy mechanism with your custom equal/copy methods. I did that in my work and there are still subtle bugs that creep months later. It gets even hard to detect when it is buried deep inside your business logic (when some decisions have to be made based on the equality comparison). |
I support the idea of limiting copying allocation (many user-defined types are invalid on copy) but not redefining it via implicit copy constructor. I'm drafting a proposal re default initializers on user-defined types, for both original & copying allocation. BTW, these are the cases of copying allocation; assignment is a different case:
|
Although this proposal is simpler than #21604, the same counter-arguments apply. We are not going to make this change. |
Overview
User-defined data types may require custom equality and copy mechanism. It would be great if there is a way to override the equality operator (==) and the copy mechanism.
Equality
Upon encountering the equality operator ==, the compiler will check whether the object implements the following method:
If the object implements the method, the method will be used for comparison. Otherwise, it will default to the standard type comparison.
Copying
Upon encountering the type copying mechanism, whether it is struct re-assignment or passed by value or by any other means I haven't thought about,
the compiler will check whether the object implements the following method:
If it implements the method, the method will be called to copy the object. Otherwise, it performs shallow copying.
Benefits
Readability
Equality comparison is one of the fundamental operations that one may perform to a type. The default equality comparison is inadequate to cover all cases, especially for complex user-defined data types. Each library ends up implementing its own way to allow user-defined equality. For instance, gomock uses Matches method. My own internal library uses Equal method. By unifying these approaches into a standard way of doing things, it improves readability. Readers would know what to expect and where to look for equality comparison.
It also allows uniformity. Instead of seeing something like this:
it becomes something like this
Scalability
One of the benefits of using Go is that it is a very forgiving language to design myopia. It is a common situation to not being able to see the whole picture when starting a project. It is only later that you realize you make mistakes in earlier design. Go allows code refactoring with minimal changes to existing code. I believe this proposal will enhance this aspect of Go.
In my projects, I often make changes to existing types. What was previously adequate with the default equality comparison (==), is now no longer valid. Hunting down every uses of this == operator in order to replace it with a custom equality method is tedious and error prone. With this proposal, one can simply implements the Equal method and all existing comparison using the == operator will work as intended. There will no longer be a need to replace every instance of == operator on the type.
The same goes with the copying operation, and it is often more tedious with the copying operation.
No Copying
Some types may be designed to not allow copying. For instance, it may contain a mutex. By implementing the Clone method and make it panics, we can prevent accidental copying the given type.
Backward Compatibility
The only potential problem is when there is a method with the same name as the method override (Equal and Clone) but with a different signature. The compiler will complain about method redeclaration as Go does not support method overloading. Otherwise, I don't think there is any compatibility problem with existing code. Users who already use compatible Equal or Clone method can continue to call it directly.
Let me know what you think.
Thanks.
Henry
The text was updated successfully, but these errors were encountered: