6 ms·
Counterpoint: coming from a Java background, I find totally freely-definable interfaces for some features used by core container types to be a little worrysome.
by heavenlyhash 8y ago
Counterpoint: coming from a Java background, I find totally freely-definable interfaces for some features used by core container types to be a little worrysome.
In Java, I can't count the number of times I've debugged either correctness bugs or 'merely' horrible performance bugs based on incorrect implementations of Equals or Hashcode methods. Implementing a non-commutative Equals method is a colossal footgun, and incredibly irritating to debug, and yet somehow it happens time and again.
And there are other, subtler, arguably "correct" and yet but extremely unwise things one can when do given Java-like maps implemented on the Equals and Hashcode interface methods. For example, one can define two types T1 and T2 which both claim to be (commutatively) equal, and define some interface TX which is implemented by T1 and T2. Using a Map<TX,Object> and inserting members by T1 and T2 now has an interesting form of semi-defined behavior: if inserting "equal" keys, whichever type was used first will be persisted. Imagine doing this in some concurrent map, and imagine that T1 is 8 bytes and T2 is 400 bytes; enjoy debugging those occasional OOMs.
I've been very happy not to ever have this particular problem when defining the key types in my Go maps.
- zlynx 8y agoGo could automatically create hidden Test methods for methods used by core types like maps. If Equals and Hashcode have to be correct, then these tests could ensure it.
- dtech 8y agoJava does equals and hashCode horribly. Granted, they did not have Java to learn from but it's what we have. Kotlin and Scala solve the problem pretty decently. Equals and hashCode are automatically generated for data/case classes. I've never encountered a messed up equals in Scala and only very rarely felt the need to override the generated equals, and then you make a very conscious choice to do it. C# does it even better, there is a IEquatable<T> interface that defines the `bool Equals(T other)` method. It partially solves the irritating universal equality. If I would design Java today Object would have no methods and there would be a stdlib interface defining `equals(other: T)`, and data classes would automatically implement that interface
- naasking 8y ago> If I would design Java today Object would have no methods and there would be a stdlib interface defining `equals(other: T)`, and data classes would automatically implement that interface This polymorphic equality is the default in some functional languages, like OCaml. It's fine for simple cases, but you really do need overloadable equality. Equality as a method in OO is a problem because of the asymmetry though, and often an even bigger problem because such languages typically permit pervasive null.
- dtech 8y agoThere definitely are cases where that's not enough, but it's a 95-99% solution. The problems you mentioned: Classes could change equals to accept subclasses. Then subclasses could not alter equality themselves. T implementing `equals(other: U)` for some U solves a lot of other use-cases, but could make equality asymetrical. Reference equality would still exist, but would not be the default, e.g. Java's `equals` is Scala's `==`, and Java's `==` is Scala's `eq` operator (which you almost never use)
- naasking 8y agohttps://blog.janestreet.com/the-perils-of-polymorphic-compare/ https://blog.janestreet.com/the-perils-of-polymorphic-compar...
- hellofunk 8y agoI recently had to write my own equals and hashcode functions in a Java project (a language relatively new to me) just so I could store my custom object as a key in a HashMap. The experience of doing this seemed so silly and unnecessarily low-level to me, that I almost abandoned the whole codebase and looked for another language to start over.
- JepZ 8y agoWell, there is another problem with my suggestion: At the moment, Maps have no problem using custom structs as they stack up the comparable attribute of the containing fields. If you would use a normal interface you would have to define how you would want to name that kind of relation (all fields of X implement interface Y so X implements Y? Sounds pretty similar to how the equal operator works in Go). Otherwise, you would have to implement Comparable for every custom type which would be in fact even worse than the current state (It is bad enough we have to do that for the sort.Interface twice per week ;-). So I completely agree, that having to implement basic attributes like 'equals' or 'comparable' is no good idea. Instead I would favor some logic which assumes some natural situation and lets you implement a custom logic if you want to do it. Nevertheless, I think those problems just show some of the inconsistencies Go comes with. Don't get me wrong, I love Go and I like the way the Go devs work. It is just that I am worried that we will end up with Go 2.0 adding Generics without solving the issues within the otherwise pretty nice Interface system.
- deleted 8y ago[deleted]