Finding the spirit of L-World
Kevin Bourrillion
kevinb at google.com
Fri Feb 22 19:42:48 UTC 2019
Fair point that `==` has always been the test of *absolute* substitutability.
But I think this is overlooking something big: People implement equals() in
order to ask for "substitutability for virtually all intents and purposes".
Of course, most code should never be going anywhere near identity hash maps
or synchronizing on value-like things, etc. And that means that equals()
has become the substitutability test that people WANT.
This in turn means that every usage of `==` on a non-primitive type (named
class) is always suspicious. As a reader and maintainer of code, I need to
think about this carefully. Is it a Class<?> -- if so == is harmless but
also .equals() is harmless and it's not worth switching idioms. Is it an
enum type? I have to go look it up to find out, in which cause it is once
again both harmless and pointless (especially if I can replace with
switch!). Barring those, then it's either a risky micro-optimization or
some other bizarre coding choice that I need to be very careful around.
I think we should make users write `equals` to test value types. If they
write `==`, they are indicating a special situation where they need
identity semantics, which don't make sense for value types, and that should
be an error.
One of the concerns I've always had about value types is that developers
would be forced to maintain a mental database of which types are value
types and which are reference types, and that they could not hope to assess
the correctness of code they read or write without having that. In a world
where users commonly need to do "absolutely substitutable" checks, then
this proposal would be the way to achieve that. But, I don't think that's
the world we're in.
Thoughts?
On Thu, Feb 21, 2019 at 9:59 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> More on substitutibility and why this it is desirable...
>
> > #### Equality
> >
> > Now we need to define equality. The terminology is messy, as so many
> > of the terms we might want to use (object, value, instance) already
> > have associations. For now, we'll describe a _substitutability_
> > predicate on two instances:
> >
> > - Two refs are substitutable if they refer to the same object
> > identity.
> > - Two primitives are substitutable if they are `==` (modulo special
> > pleading for `NaN` -- see `Float::equals` and `Double::equals`).
> > - Two values `a` and `b` are substitutable if they are of the same
> > type, and for each of the fields `f` of that type, `a.f` and `b.f`
> > are substitutable.
> >
> > We then say that for any two objects, `a == b` iff a and b are
> > substitutable.
>
> Currently, our type system has refs and primitives, and the == predicate
> applies on all of them. And for all the types we have today (with the
> almost-too-small-to-mention anomaly of NaN), == *already is* a
> substitutibility predicate (where substitutibility means, informally:
> "no observable difference between the two arguments." Two refs are
> substitutible if they refer to the same object identity; two primitives
> are substitutible if they refer to the same value (modulo NaN.)
>
> VM engineers like to refer to `==` on refs as "identity equality", but
> that's really an implementation detail. What it really means is: are
> the two things the same. And that's what `==` means for primitives too,
> and that's how the other 99.99% of users think of it too.
>
> The natural interpretation of `==` in a world with values is to extend
> this "are these two things the same" to values too. The
> substitutibility relation above applies the same "are you the same"
> logic equally to refs, values, and primitives. No sharp edges (except
> the NaNsense that we are already stuck with.)
>
>
>
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
More information about the valhalla-spec-observers
mailing list