Andrea Giuliano
10/01/2020, 12:44 PMstreetsofboston
10/01/2020, 12:52 PMAndrea Giuliano
10/01/2020, 12:54 PMdata class User(id: String, name: String, age: Int): Comparable<User>
is there a way to leverage the default comparison field by field?Andrea Giuliano
10/01/2020, 12:54 PMstreetsofboston
10/01/2020, 1:02 PMget
them for each of the two instances and handle the result [-1, 0, 1].Andrea Giuliano
10/01/2020, 1:03 PMVampire
10/01/2020, 1:05 PMdata class User(val id: String, val name: String, val age: Int) : Comparable<User> {
override fun compareTo(other: User) =
compareBy(User::id, User::name, User::age)
.compare(this, other)
}
?Vampire
10/01/2020, 1:06 PMdata class User(val id: String, val name: String, val age: Int) : Comparable<User> {
override fun compareTo(other: User) = comparator.compare(this, other)
companion object {
private val comparator = compareBy(User::id, User::name, User::age)
}
}
streetsofboston
10/01/2020, 1:09 PM(T) -> Comparable<*>
, @Vampire's answer looks great.Vampire
10/01/2020, 1:14 PMcompareBy<User>({ it.id }, { it.name }, { it.age })
Nir
10/01/2020, 1:23 PMNir
10/01/2020, 1:23 PMAndrea Giuliano
10/01/2020, 1:24 PMVampire
10/01/2020, 1:24 PMAndrea Giuliano
10/01/2020, 1:24 PMNir
10/01/2020, 1:24 PMAndrea Giuliano
10/01/2020, 1:25 PMdata class User(val id: String, val name: String, val age: Int, val metadata: Map<String, String>)
Nir
10/01/2020, 1:25 PMVampire
10/01/2020, 1:25 PMVampire
10/01/2020, 1:26 PMAndrea Giuliano
10/01/2020, 1:26 PMVampire
10/01/2020, 1:26 PMNir
10/01/2020, 1:27 PMNir
10/01/2020, 1:27 PMNir
10/01/2020, 1:28 PMNir
10/01/2020, 1:28 PMVampire
10/01/2020, 1:28 PMdata class User(val id: String, val name: String, val age: Int, val metadata: Map<String, String>) : Comparable<User> {
override fun compareTo(other: User) = comparator.compare(this, other)
companion object {
private val comparator = compareBy(
User::id,
User::name,
User::age,
{ generate a comparable value out of the map here }
)
}
}
Nir
10/01/2020, 1:29 PMVampire
10/01/2020, 1:30 PMWell, the logical thing to do is to simply make it non comparable if it has a comparable fieldThat doesn't make sense
you have the same issue with equality, in principleNo, because everything can calculate equality, but not everything is comparable
looks like kotlin silently falls back to identity based equalityAs does Java and any other similar language.
Nir
10/01/2020, 1:30 PMNir
10/01/2020, 1:31 PMVampire
10/01/2020, 1:31 PMNir
10/01/2020, 1:31 PMNir
10/01/2020, 1:31 PMVampire
10/01/2020, 1:31 PMVampire
10/01/2020, 1:32 PMNir
10/01/2020, 1:32 PMNir
10/01/2020, 1:32 PMNir
10/01/2020, 1:33 PMstreetsofboston
10/01/2020, 1:33 PM===
?Nir
10/01/2020, 1:33 PMNir
10/01/2020, 1:34 PMis
for this, for example, though is
is already something much more useful obviouslyVampire
10/01/2020, 1:34 PMis
Nir
10/01/2020, 1:34 PMis
can be used for thatNir
10/01/2020, 1:34 PMNir
10/01/2020, 1:34 PMVampire
10/01/2020, 1:35 PM===
streetsofboston
10/01/2020, 1:35 PMNir
10/01/2020, 1:35 PMVampire
10/01/2020, 1:36 PMequals
, why would anyone think it is a good idea to return false
if you compare something to itself?streetsofboston
10/01/2020, 1:36 PMa == b
is short for a.equals(b)
Nir
10/01/2020, 1:36 PMVampire
10/01/2020, 1:36 PMNir
10/01/2020, 1:36 PMNir
10/01/2020, 1:36 PMNir
10/01/2020, 1:36 PMVampire
10/01/2020, 1:37 PMVampire
10/01/2020, 1:37 PMVampire
10/01/2020, 1:37 PMNir
10/01/2020, 1:37 PMNir
10/01/2020, 1:37 PMNir
10/01/2020, 1:38 PMVampire
10/01/2020, 1:38 PMa == b
is a?.equals(b) ?: (b === null)
😉Nir
10/01/2020, 1:38 PMNir
10/01/2020, 1:38 PMNir
10/01/2020, 1:38 PMVampire
10/01/2020, 1:38 PMVampire
10/01/2020, 1:39 PMwhy is it that comparing unrelated types fails to compile, instead of returning false?is it?
Nir
10/01/2020, 1:39 PMNir
10/01/2020, 1:39 PMstreetsofboston
10/01/2020, 1:39 PM"hello" == 5
fails to compile, since it will always return false.
But if you upcast the instances of both sides to Any
, it will compile and return falseVampire
10/01/2020, 1:40 PMfalse
, so where is the point in comparing them?Vampire
10/01/2020, 1:40 PMNir
10/01/2020, 1:40 PMstreetsofboston
10/01/2020, 1:41 PM"hello" == 5 // Error
"hello" as Any == 5 as Any // Compiles fine
Nir
10/01/2020, 1:41 PMNir
10/01/2020, 1:41 PMVampire
10/01/2020, 1:41 PMNir
10/01/2020, 1:42 PMVampire
10/01/2020, 1:42 PMVampire
10/01/2020, 1:42 PMNir
10/01/2020, 1:42 PMNir
10/01/2020, 1:43 PMdata class
for example, I'd expect that constructing it twice with identical arguments, should give me two equal instances. That's what a well behaved value type looks like.Vampire
10/01/2020, 1:43 PMNir
10/01/2020, 1:44 PMstreetsofboston
10/01/2020, 1:46 PMEq
typeclass for a type to be able to check for equality… Instead, it chose to provide the equals
method on the top-most object Any
that can be overridden and its default impl is this === that
. Different choices were made.Nir
10/01/2020, 1:46 PMVampire
10/01/2020, 1:47 PMNir
10/01/2020, 1:47 PMVampire
10/01/2020, 1:49 PMVampire
10/01/2020, 1:49 PMNir
10/01/2020, 1:49 PMNir
10/01/2020, 1:50 PMNir
10/01/2020, 1:51 PMAndrea Giuliano
10/01/2020, 5:06 PMpublic int compare(Map<String, String> first, Map<String, String> second) {
Iterator<Map.Entry<String, String>> firstIterator = first.entrySet().iterator();
Iterator<Map.Entry<String, String>> secondIterator = second.entrySet().iterator();
while (true) {
Boolean firstHasNext = firstIterator.hasNext();
Boolean secondHasNext = secondIterator.hasNext();
int result = firstHasNext.compareTo(secondHasNext);
if (result != 0 || !firstHasNext) {
return result;
}
// we have two entries to compare
Map.Entry<String, String> n1 = firstIterator.next();
Map.Entry<String, String> n2 = secondIterator.next();
result = n1.getKey().compareTo(n2.getKey());
if (result != 0) {
return result;
}
result = n1.getValue().compareTo(n2.getValue());
if (result != 0) {
return result;
}
}
}
I’m still trying to figure out a way to make that looks nicer with the kotlin snippet you suggested which I believe is very elegantAndrea Giuliano
10/02/2020, 8:32 AMprivate val keyComparator =
compareBy<Map.Entry<String, String>> { it.key }
private val keyThenValueComparator =
keyComparator.thenComparator { a, b -> compareValues(a.value, b.value) }
my only missing bit there is how to replicate the check done in the snippet above on the sizing, anyone has any suggestion?Vampire
10/02/2020, 9:03 AMdata class User(
val id: String,
val name: String,
val age: Int,
val metadata: Map<String, String>
) : Comparable<User> {
override fun compareTo(other: User) = comparator.compare(this, other)
companion object {
private val comparator = compareBy(
User::id,
User::name,
User::age,
{ user ->
user
.metadata
.entries
.flatMap { it.toPair().toList()}
.joinToString("\u0000")
}
)
}
}
Vampire
10/02/2020, 9:29 AMdata class User(
val id: String,
val name: String,
val age: Int,
val metadata: Map<String, String>
) : Comparable<User> {
override fun compareTo(other: User) = comparator.compare(this, other)
companion object {
private val entryComparator: Comparator<Entry<String, String>> = compareBy(
{ it.key },
{ it.value }
)
private val comparator = compareBy(
User::id,
User::name,
User::age,
).thenBy(Comparator { entries1, entries2 ->
entries1.asSequence()
.zip(entries2.asSequence())
.map { (entry1, entry2) -> entryComparator.compare(entry1, entry2) }
.filter { it != 0 }
.firstOrNull()
?: (entries2.size - entries1.size)
}) {
it.metadata.entries
}
}
}
Andrea Giuliano
10/02/2020, 9:31 AMVampire
10/02/2020, 10:11 AMAndrea Giuliano
10/02/2020, 3:33 PMFleshgrinder
10/04/2020, 8:36 AMVampire
10/04/2020, 8:52 AMFleshgrinder
10/04/2020, 9:03 AMEq
, PartialEq
, Debug
, Display
, Hash
, ...Vampire
10/04/2020, 9:08 AMObject
and Any
and inherited by subclasses. Almost all you said is true for any inherited behavior, so to solve generically it you would have to cancel out inheritance completely.
I still don't see why it should be a good idea to not have something equal to itself.Fleshgrinder
10/04/2020, 9:11 AM===
operator and totally sensible to be there by default since this only has to do with memory effects. Allowing this to be overwritten would actually be unsound. No, this is about equality and what equality means. In equality something is not necessarily equal to itself (e.g. NaN
and NaN
).Vampire
10/04/2020, 9:27 AMFleshgrinder
10/04/2020, 9:33 AMVampire
10/04/2020, 9:35 AMFleshgrinder
10/04/2020, 9:40 AM00:00 +00:00
the same as 01:00 +01:00
or 00:00 +24:00
?Andrea Giuliano
10/04/2020, 10:40 AMdata class User(
val id: String,
val name: String,
val age: Int,
val metadata: Map<String, String>
) : Comparable<User> {
override fun compareTo(other: User) = comparator.compare(this, other)
companion object {
private val entryComparator: Comparator<Entry<String, String>> = compareBy(
{ it.key },
{ it.value }
)
private val comparator = compareBy(
User::id,
User::name,
User::age,
).thenBy(Comparator { entries1, entries2 ->
entries1.asSequence()
.zip(entries2.asSequence())
.map { (entry1, entry2) -> entryComparator.compare(entry1, entry2) }
.filter { it != 0 }
.firstOrNull()
?: (entries1.size - entries2.size)
}) {
it.metadata.entries
}
}
}
Vampire
10/04/2020, 11:10 PMVampire
10/04/2020, 11:12 PMother classical examples usually deal with time, is 00`:00 +00:00 t`he same as 01`:00 +01:00 o`r 00`:00 +24:00?`That's totally not the question though. The point is, why should
00:00 +00:00
not be equal to 00:00 +00:00
Fleshgrinder
10/05/2020, 6:26 AMVampire
10/05/2020, 7:07 AMFleshgrinder
10/05/2020, 7:13 AMequals
implementation uses identity:
> looks like kotlin silently falls back to identity based equality
As does Java and any other similar language.@Nir didn't agree with you at which point you got aggressive but @streetsofboston jumped in and also agreed that this behavior is not sensible, so did I later. I think the situation is very clear, have a nice day.
Vampire
10/05/2020, 7:16 AM