compareTo and deriving
Kevin Bourrillion
kevinb at google.com
Wed Dec 6 19:39:46 UTC 2017
For what it's worth the AutoValue FAQ says this:
How do I also generate compareTo?
AutoValue intentionally does not provide this feature. It is better for you
to roll your own comparison logic using the new methods added to Comparator
<https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html> in
Java 8, or ComparisonChain
<http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/ComparisonChain.html>
from Guava <http://github.com/google/guava>. Since these mechanisms are
easy to use, require very little code, and give you the flexibility you
need, there's really no way for AutoValue to improve on them!
(The "flexibility you need" having actually just been described in Brian's
last message.) I think it holds equally for records in the language.
On Tue, Dec 5, 2017 at 10:17 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> At some level, this goes to the heart of "why should records, and not
> tuples, be our 'plain data' abstraction." Adding behavior to a record,
> **where that behavior is derived strictly from the state**, is easy and
> natural.
>
> You can look at the "but I have to write compareTo" issue as glass 90%
> full or 10% empty; a record starts life with a sensible ctor, dtor,
> accessors, equals, hashCode, and toString. So adding compareTo is pretty
> easy. Note also that there's no way we can reasonably derive compareTo
> from the state description, since (a) not all state fields are going to be
> relevant for ordering comparison and (b) the order in which they are
> declared may not be the natural comparison order. So somewhere in the
> program the user is going to have to write down that "a Person is compared
> by (lastName, firstName)", somehow.
>
> We could of course invent all sorts of shorthands, but I think the
> return-on-complexity there is limited, as the natural way to say this is
> pretty straightforward:
>
> record Person(String first, String last, String age)
> implements Comparable<Person> {
>
> private static final Comparator<Person> cc
> = Comparators.comparing(Person::last).thenComparing(Person::fi
> rst);
>
> public int compareTo(Person other) {
> return cc.compareTo(other);
> }
> }
>
> But, let's not get distracted by Billy, we care about semantics. Where are
> the pitfalls you see of this approach where it might run afoul of the
> "consistency between equals and compareTo is recommended" dictum?
>
>
>
> On 12/5/2017 3:12 AM, forax at univ-mlv.fr wrote:
>
>> So let restart by trying to untangle things.
>>
>> First question, should we only support Object methods (toString, equals,
>> hashCode) or more, again here, two sub questions,
>> it can be only (toString, equals, hashCode) in the first release and more
>> in a subsequent release.
>> Why more, the question is where to put the bar, if we do only toString,
>> equals, hashCode, people will ask for compareTo (i don't think there is
>> another method than compareTo but i may be wrong). The rational to
>> introduce compareTo is that the semantics of compareTo is linked to the
>> semantics of equals which is generated, so asking someone to write
>> compareTo given that he doesn't have to write equals is clunky. There are
>> several reason to not introduce compareTo. toString, equals, hashCode cover
>> most of the case and we have to stop somewhere, people tend to use
>> compareTo at a place where they should use a Comparator, i.e. the order
>> defined by compareTo is not a 'natural' order.
>>
>> Second question, how to specify the mapping between toString, equals,
>> hashCode and their implementation ? Should we have a way to extends the
>> mapping if we want to introduce compareTo later without having to change
>> the JLS. Again, sub-questions, Should this mapping being explicit like in
>> Haskell with deriving or should it be implicit, there is no real need to
>> specify a mapping for the method for j.l.Object because those method
>> declaration are already present, and if the record implements Comparable it
>> means that we have to provide the implementation of compareTo.
>>
>> regards,
>> Rémi
>>
>> ----- Mail original -----
>>
>>> De: forax at univ-mlv.fr
>>> À: "Brian Goetz" <brian.goetz at oracle.com>
>>> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
>>> Envoyé: Lundi 4 Décembre 2017 16:12:58
>>> Objet: Re: compareTo and deriving
>>> ----- Mail original -----
>>>
>>>> De: "Brian Goetz" <brian.goetz at oracle.com>
>>>> À: "Remi Forax" <forax at univ-mlv.fr>, "amber-spec-experts"
>>>> <amber-spec-experts at openjdk.java.net>
>>>> Envoyé: Lundi 4 Décembre 2017 15:54:13
>>>> Objet: Re: compareTo and deriving
>>>> As you might remember from the EG meeting, we have toyed with a notion
>>>> of "method builders" which would be a more declarative way to declare
>>>> template-izable methods. The intent all along was that, once we have
>>>> such a mechanism, records would just be a consumer of such a mechanism.
>>>>
>>>> That said, such a mechanism is bigger both from a technical perspective
>>>> and a bikeshed perspective. 90% of the current OO-ceremony-pain is in
>>>> ctors, Object methods, and accessors. So my preference would be to nail
>>>> down the semantics of data classes first, and then explore whether it
>>>> makes sense to surface a more general mechanism.
>>>>
>>> I'm trying to propose a middle ground (not too far from only support
>>> Object
>>> methods) to allow to not have too much couple between the JLS and the
>>> metafactory methods.
>>>
>>> One advantage i see to have compareTo generated is that most
>>> implementation i
>>> see of the compareTo are broken either because of the primitive
>>> overflowing or
>>> compareTo and equals not having compatible semantics. One problem we
>>> will have
>>> if we let people to write compareTo is that it will be harder to get it
>>> right
>>> because equals will be generated while compareTo will be written manually
>>> without seeing the code of equals.
>>>
>>> Rémi
>>>
>>>
>>>> On 12/4/2017 8:34 AM, Remi Forax wrote:
>>>>
>>>>> Ok,
>>>>> record/data class currently provides getters and
>>>>> toString/equals/hashCode, what
>>>>> if i want to add a compareTo.
>>>>>
>>>>> In Haskell, it's easy, one can use 'deriving', in Java, it can be
>>>>> written that
>>>>> way,
>>>>> record Point(int x, int y) implements Comparable<Point>
>>>>> deriving equals, hashCode, toString, compareTo;
>>>>>
>>>>> For the compiler, the method exists in the supertypes, so the
>>>>> signature of the
>>>>> method can be computed from the super types,
>>>>> after each generated method use an invokedynamic with the same meta
>>>>> protocol (a
>>>>> list of getters),
>>>>> so if by convention "equals", "hashCode", "toString", "compareTo" are
>>>>> the names
>>>>> of some bootstrap methods in a class DerivingMetaFactory,
>>>>> it can be trivial for a JDK developer to add a new bootstrap method
>>>>> without
>>>>> having to change the JLS.
>>>>>
>>>>> So my point is, that we can specify once how the compiler should
>>>>> generate codes
>>>>> for any generated methods in an extensible way.
>>>>>
>>>>> regards,
>>>>> Rémi
>>>>>
>>>>
>
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20171206/a25a0a7b/attachment-0001.html>
More information about the amber-spec-experts
mailing list