4 ms·
That's pretty much saying "it is the way it is", which a) is not even true, see floating point numbers and b) will be obsolete with value types. Needless to
by simon_o 9y ago
That's pretty much saying "it is the way it is", which
a) is not even true, see floating point numbers
and
b) will be obsolete with value types.
Needless to say, there is pointless confusion created by Java's design, and there are better approaches available.
All you have to do is to adapt the semantic model from reference equality vs. value equality to identity vs. equality.
Identity checks whether the "bits" are identical, irregardless of whether the bits are "references" or values, and equality is a user-defined operation.
"Foo" equality "Foo" // True
"Foo" identity "Foo" // Only true if they point to the same "object"
123 equality 123 // True
123 identity 123 // True
Double.NaN equality Double.NaN // False
Double.NaN identity Double.NaN // True
Which symbols you pick for "equality" and "identity" is largely arbitrary.
- int_19h 9y agoBetter yet, allow identity checks for reference types only. Value types don't have identity, per se, so the operation should be meaningless for them.
- simon_o 9y agoI don't think you can get away with that for theoretical and practical reasons: 1. There is a ton of code out there which does something like def contains(that: Thing): Boolean = this.value identity that || this.value equality that Pretty much every single collection implementation would be broken if this stopped working with value types. Additionally, you would run into issues with floating point numbers which would not be found/retrieved anymore if identity were removed. 2. The idea to define a _sane_ definition of identity/equality across all types is there to avoid the "next-best" option: boxing primitives to wrapper classes which is both slow and has terrible semantics. 3. I don't really think restricting identity to e.g. reference types makes sense given that equality is defined for every type. Either none of them should be available by default, or both should be. There _are_ multiple valid ways to compare to things (consider floating point numbers for a second) and making one more privileged than the other feels wrong.
- lmm 9y ago> Pretty much every single collection implementation would be broken if this stopped working with value types. Maybe collections of values should be different from collections of references. The sensible use cases for the two are quite different. > Additionally, you would run into issues with floating point numbers which would not be found/retrieved anymore if identity were removed. Meh, just allow NaN to compare equal to itself. Equality is supposed to be reflexive. > The idea to define a _sane_ definition of identity/equality across all types is there to avoid the "next-best" option: boxing primitives to wrapper classes which is both slow and has terrible semantics. Unboxed primitives don't have identity, only value equality. They align well with what's being proposed. > I don't really think restricting identity to e.g. reference types makes sense given that equality is defined for every type. Either none of them should be available by default, or both should be. We could build the distinction into the language, so for every type you define you explicitly choose whether it's value or reference. Scala's already halfway there with the class/case class distinction. > There _are_ multiple valid ways to compare to things (consider floating point numbers for a second) Disagree; comparison is so fundamental to most types that it's worth privileging. Using the wrong kind of comparison is a very common source of bugs.
- simon_o 9y ago> Maybe collections of values should be different from collections of references. The sensible use cases for the two are quite different. I think all existing code disagrees with that. There has been great value derived from being able to abstract over element types. What you are proposing would double the required number of collection classes and all of its traits, because it would require separate ones for Collection[E <: AnyRef] and for Collection[E <: AnyRef]. There is literally no reason for introducing this complexity. Go has demonstrated how poorly this idea has worked out in practice. Additionally, this approach would make it nearly impossible to migrate reference types to value types, because it would break all users of the code. > Meh, just allow NaN to compare equal to itself. Equality is supposed to be reflexive. That's a complete non-option. You might not like the IEEEs definition of equality, but this is what it is. Messing with it would break all existing code using floating point numbers. > Unboxed primitives don't have identity, only value equality. Their identity is the bits they consist of, just like identity on references is the bits of the reference. > They align well with what's being proposed. What is being proposed? > We could build the distinction into the language, so for every type you define you explicitly choose whether it's value or reference. We already have that: AnyRef and AnyVal. > Scala's already halfway there with the class/case class distinction. That doesn't make any sense. The case keyword is basically just a compiler built-in macro to generate some code. It is already doing way to much, and overloading it with even more semantics is not the way to go. > Disagree; comparison is so fundamental to most types that it's worth privileging. Using the wrong kind of comparison is a very common source of bugs. What I'm proposing improves the consistency across value and reference types so that it's always obvious which kind of comparison happens: - identity: Low-level comparison of the bits at hand. Built into the JVM and not overridable. - equality: High-level comparison defined by the author of the type.