Open FroMage opened 12 years ago
This would be doable if/when we have some kind of lazy evaluation.
Of course, we could generalize the ||
and &&
operators to some kind of Truthy<T>
interface that is satisfied by both Boolean
and Comparison
. But I'm not sure if I like that idea.
If we do that we'll have requests for Object foo = objA || objB;
If we do that we'll have requests for
Object foo = objA || objB;
We already have that, don't we? The ?
operator.
Doesn't matter that we do, people will want non-null values to be Truthy
.
Doesn't matter that we do, people will want non-null values to be
Truthy
.
:-(
I'm arguing against myself here of course, I learn quick :)
OK, so indeed there is a reasonable solution to this problem. It would be to change the definition of <=>
, and I guess also of the compare()
method of Comparable
to evaluate to null
when the values are equal. So you could write your example as:
Comparison? compare(Person other) {
return firstName<=>other.firstName
else lastName<=>other.lastName
else age<=>other.age;
}
This is noticeably cleaner than either Java or Perl.
If we did this, I guess I would also be inclined to have <=>
accept null operands, evaluating to null
in the case that either operand is null.
Actually if we didn't want to go with this <=>
evaluating to null
thing, we could add the following function
Comparison? unequal(Comparison comparison) { return comparison!=equal then comparison; }
Letting you write:
Comparison compare(Person other) {
return unequal(firstName<=>other.firstName)
else unequal(lastName<=>other.lastName)
else unequal(age<=>other.age)
else equal;
}
Which is more verbose but leaves intact the definition of Comparison
.
One thing that plays into this discussion is the question of partial orders, like what we have with Person
. In that case, the set of outcomes of compare() should include "not comparable", i.e. null
. Not quite sure how this consideration changes things.
Hrm, why not introduce a <>
operator which returns larger
, smaller
, or null
, leaving the definition of <=>
alone?
Comparison? compare(Person other) {
return firstName<>other.firstName
else lastName<>other.lastName
else age<>other.age;
}
I like this. A lot.
Look, honestly I can live with unequal
, and I don't think we can spend a new operator out of operator budget for such a specific corner-case. I like Perl's daisy chaining, but I don't think it fits in Ceylon, as it is a consequence of truthy/fasly throughout the whole language. Having <=>
return smaller/null/larger also feels weird, but perhaps @emmanuelbernard, @quintesse or @tombentley have a different opinion?
I definitely don't think we should make <=>
return null
in the case of equal
. I mean, the actual characters themselves look like they are saying "less than, equal, or greater than". And by the same token, I think it's totally natural to introduce <>
to mean "less than, or greater than".
The way I look at it is you can chain comparisons in equals()
using ==
and &&
. It feels to me pretty natural to be able to do the same thing in compare()
using <>
and else
.
I agree there's a lot of boilerplate in writing a comparator. I don't find the whole null
returning <>
operator very intuitive though. I agree with @FroMage that it feels like we're catering for a quite specific use case here and I think people will get confused by having two very similar operators serving almost the same purpose.
It would be nice if there was some way we could generate a comparator just given the attributes which should be involved, (and if the compiler could optimize this for the common case of the attributes being known at compile time, even better). Even if the compiler couldn't do this, it should be provided by the IDE (if it's not already).
I think reasonable alternative would be to provide something akin to Google's ComparisonChain.
Then we can just write something like this:
shared actual Comparison compare( Other other ) {
return ComparisonChain()
.compare(this.name, other.name)
.compare(this.age, other.age)
.compare(this.methodName, other.methodName)
.result();
}
It's just a raw idea. there is probably a more Ceylonic
BTW: I must admit treating result of <=>
operation as a Truthy/Falsy value in the context of else
statement does make a whole lot of sense.
For all intents and purposes, in the meantime if someone stumbles upon this discussion and is looking for a temporary solution for basic use cases, this is a simple Ceylon class that mimics the new Java 8 Comparator#comparing
:
https://gist.github.com/sgalles/e5e8cfe63f4e1a6debf5
It allows to write :
value comparator = Comparing{
byIncreasing(Person.lastName),
byIncreasing(Person.firstName)
}.comparator;
(update : edited to use the second, more ceylonic version of the class)
The #1 biggest let down with comparison operators like
<=>
orComparable.compareTo()
in Java is that you can't easily daisy-chain them:When in Perl you'd write:
Because boolean operators like
||
return truthy values (-1
and1
are truthy while0
isfalsy
), and<=>
returns a number;We should find a way to support this somehow, in a style compatible with Ceylon, because writing comparators is a very frequent task.