Java Enums are Comparable by default? :dizzy_face:...
# language-proposals
m
Java Enums are Comparable by default? 😵 If Kotlin should ever introduce their own
enum
type (instead of
enum class
) then please don't do that 🙏
m
That's the Java enum.
Or an
enum class
to be more specific.
m
That class is a common class. It just maps to Java enum on the JVM.
m
still it's a class
I still hope that because there's
enum class
there could be a non-class
enum
in the future 😄
m
What difference does that make?
m
To be a primitive instead of an object 🤔 And non-Comparable by default. Maybe even without having an ordinal by default. And maybe optionally open.
m
I don't get what that would solve that today's enums doesn't solve. Also, how do you represent a primitive enum in memory without an ordinal? A string value?
m
I could also solve everything in Java so why use Kotlin 🤷‍♂️ The thing is that Java's enum do too much and that can be improved. Currently • it's a reference type instead of a value type. • it has
ordinal
,
name
,
valueOf
,
Comparable
(and maybe others) defined by default which is not desirable. • it has a recursively generic abstract superclass. A String would be a reference type again, including related overhead. In memory it could be an primitive int, a memory address or whatever - I don't care nor need to know as a developer. Similar to inline classes and properly implemented in the JVM with project Valhalla.
r
I have no idea whether
Comparable
is expected on these or not, but regarding primitives and enums you can check this: https://youtrack.jetbrains.com/issue/KT-23823
r
Why is it not desirable? What's wrong with enums being comparable?
1
I find it quite useful, for example in a logger
Copy code
fun log(level: Level, message: String) {
    if (level > threshold) ...
}
1
The whole point of an enum is to enumerate some values, which implies an intrinsic order.
m
No it doesn't. It could also be a set of options to choose from. It makes absolutely no sense to bring them in any order. Enumerating doesn't imply order. Just because comparable names sense in some cases it doesn't make sense to make Enums comparable by default.
Just like with Collection and List. For a Collection order of elements may or may not be relevant. List implies an order of its elements.
r
Yes, it does. It's literally the definition of enumerate:
to ascertain the number of; to specify one after another
An enumeration is a complete, ordered listing of all the items in a collection.
m
enumerate as in Java or as in the English language?
r
As in both the English language and the vast majority of programming languages.
m
Okay, but that still doesn't make elements comparable. Just because in
listOf(2, 1)
1 comes after 2 its not automatically greater than 2.
It's just about the order while enumerating them.
r
But that is fundamental to the definition of the enum
m
No, where does it say that?
r
...in the definition
I don't mean to argue, I'm just having a hard time seeing where you're coming from, and I'm guessing you are having an equally hard time with me 🙂
m
All im saying is that it doesn't make sense for many enum values to be Comparable and apart from Enum implementing it I can't find a definition which states that its supposed to be so nor why. North > South Earth > Mars it just doesn't make any sense (and that's just about Comparable - name is even more annoying)
Only in some languages does an enum define some inherent ordering. Often it’s used as a discriminated union.
m
The latter is how I understood them, with exceptions. Assigning enums and integers and having an order came from the C world, didn't it?
r
@Marc Knaup Agreed, so why define those as enums? It would make more sense to use an algebraic data type (i.e.
sealed class
). If it doesn't make sense to say South comes after North, it doesn't make sense to enumerate it.
m
That adds even more overhead in terms of code, classes and initialization. They're not value types. Also doesn't work as constants in annotations.
Btw, the examples north/south or planets are actually from Java's documentation about enums 😅 They don't have good alternatives though.
r
Enums aren't value types either, so I don't think it actually adds more overhead (at least not appreciably). The "constants in annotations" argument is orthogonal.
Yeah, that's a failing of Java though. Kotlin fixed that issue with proper algebraic types.
m
The reason that
enum class
in Kotlin works the way it does is that it needed Java compatibility. I just suggest rethinking it as Kotlin becomes more and more its own language.
r
I think that's an incorrect assumption. Kotlin's
Any
doesn't implement the same API as Java's
object
. I would counter that
enum class
in Kotlin works the way it does because it makes sense. That's why most every language with enums does that, even if they never talk to Java or C or whatever.
m
Before inline classes there was no way to interface value types with reference types in Java hence enum needed to remain a reference type, didn't it? It also allows for complex values having method overrides. And the name & ordinal properties you also can't remove, nor the comparable interface, if you want to stay compatible.
And I'd look at modern languages rather than any language when comparing. Things change :) Swift for example doesn't have them comparable nor exposes underlying integer values.
r
Sure, but Rust does. That's why you need to look at many languages, so see the bigger trend.
m
Yeah. So the discussion should be what makes sense, not what others do or did in the past. Which brings me back to my initial messages on what should be improved :)
r
Right. That's what I'm saying. To me the current way makes sense. I see your proposal as an impairment, not an improvement, so I'm just trying to see your reasoning why you think it is an improvement so I can see if I need to rethink my assumptions.
By the way, value type and comparable aren't mutually exclusive. `Int`s are Comparable in Kotlin. That's just a matter of compiler magic, not language definition per say.
m
Why is it an impairment to remove for example a default comparable? You can still add it in cases where it makes sense. Probably for the majority of enums it doesn't make sense and not having it will make api and intent clearer.
I'm impaired by not being able to name a property
name
because it refers to Java/Kotlin identifier (the enum case) instead of the name of the thing the enum case actually describes
r
You could say the same thing about Strings or any other type.
m
No not really 🤔
r
Again, not being able to use
name
is completely orthoganal to the comparability discussion.
m
There is one single string class describing one type of data. There is an infinite amount of enum types each with different meanings
I'm not discussing compatibility though
r
Okay, you could say the same thing about
CharSequence
Never mind, I don't think
CharSequence
is comparable
I said comparability, not compatability. The question was about why they are comparable.
m
Oh, misread
r
It's all good, I do that a lot myself 🙂
m
Yeah, add autocompletion and it gets even better to have confusion than that 😅
😆 1
r
I can kind of see where you're coming from, I'm just not convinced, but obviously I'm not the one that makes the decision. I just wanted to give my input. Thanks for the discussion and the insights
m
Yeah I think you're coming from the technical side while I'm coming from the "business logic" side or whatever that would be called. What is trying to be represented and where there is confusion or limitation for whatever reason
Thank you too :)
Except value types. That's quite low level 😂
l
(Skipped the end of the thread) Enumarate implies an order per the definition in English, French, etc, and Swift is wrong to not allow accessing that order for something called an
enum
. That said, what you are looking for is
sealed class
of `object`s, with explicit inlining where the ordinal used for the underlying constant primitive is explicitly specified but private to the declaration, so it can be used for public APIs without unexpected breakages. We could also imagine a
set class
that would be like a one level
sealed class
with stateless objects only, where either order would matter, or explicit ordinal would be specified, so the compiler flattens it to primitive values for the produced binary. It could also work with something like R8 or proguard with a Kotlin specific logic to inline down to primitive all fully stateless
object
based `sealed class`es. Are you targetting Android?
m
Ideally Android should work just fine for any language feature 🤔 But yes, due to multiplatform it should work everywhere.
Whether an enumeration type implies order or not is not clear. That was part of the thread you've skipped 😛
d
I think there is no good motivation to do this, and I think it would have a negative impact on the language as a whole because of it. If you don't want any of the features of
enum class
, each of which have their use cases, you should consider using another way of declaring it.
l
I didn't skip most of the dispute around order or not in enums. To me, it's cleat that enumeration implies order, always, and anything that doesn't have this capability should be named otherwise.
d
I don't fully agree with that, as enumerations can have an undefined order, for example with legacy java enumerations of sets. But I wouldn't suggest removing it because it has good use cases.
m
We are mixing up two different things here. - iteration order of an enumeration (just like a List) - natural order of the elements (i.e. Comparable, like Int) It's fine that enums have a well-defined iteration order. But that doesn't imply that the elements need to be Comparable as it doesn't make sense for many of them. Sure it can be a good use case, but in that case you can always add Comparable on your own.
d
Or you dont have to add it because it's already there, saving time
m
Yeah, let's confuse API users by telling them that things can be compared which actually can't. Including autocompletion for that and the compiler won't even warn that it doesn't make sense to compare the two values you have.
But it's a little easier for other enum cases :)
d
Well, it's not about whether they can be compared, but whether there is a use case for it, which depends on what you're using enums for
m
Exactly! Only the person who defines the enum knows what it's supposed to express. Kotlin can't know that.
d
Right. And the way it is currently is fine. Ability to compare enum values is sometimes a nice to have and sometimes unnecessary. It's lunacy to suggest changing it or adding a new kind of enum because of it.
m
That's not what I said.
d
Oh sorry, it's what I interpreted
l
I don't understand why not just use
sealed class
. There's even an IDE intention to do the conversion in no time.
m
I said that if there will be a new enum, then it should be rethought.
l
There will probably be no new enum that doesn't enumerate, because it's against the English definition IMHO, and it'd confuse people with other enums.
m
It can enumerate, even in defined order! Just because a List's elements can be enumerated doesn't mean it's elements are Comparable. Again, two different things.
l
Not completely separate, because if you can enumerate, you can give it a number (aka. ordinal), which you can then compare, so removing the comparability of enums just makes it harder and less safe to compare two entries. Also, remember that `enum class`es can't extend other classes, so there's no possible confusion about what is being compared.
m
You could make the same argument for Lists... Because all elements can be enumerated there, they all should have a number and they all should be Comparable.
l
No, I can't for lists because their content can be anything and can itself be comparable.
m
Enum values can have a natural way to compare them too...
l
To be clear. An enum doesn't encapsulate anything. It IS, by itself, THE enumeration. A list, is a list OF things that themselves can have any comparability criterions. You can have a list of
String
, but not an enum of
String
, this doesn't make sense and is impossible. The name of the enum entry though, is a
String
, and you can also retrieve it.
They always have, that natural way is the entries declaration order.
m
And just because Lists can hold anything it doesn't change your definition if enumeration where elements have the position in the enumeration as natural order for Comparable. You make enumeration different for enum values and List.
1
"They always have" is not a good argument for anything.
The we'd not have a lot of nice things today.
l
It's not an argument, its a fact, like if you don't want natural comparability, don't use
enum class
.
Maybe that's unfortunate for your use case, but this is set in stone in Kotlin and Java APIs, and I already said why I don't think comparability should be removed from enum if those compatibility and familiraity concerns could be dropped.
m
wow, I can only repeat: I don't want to change today's enum.
l
So let's not call it enum, because it brings confusion, even for us.
m
This is about a potential scenario where something new is introduced to iterate on the old Java enum and its introduced anyway.
I don't mind if it's called differently. Swift does well with enum and developers can express intent much more clearly with them, without the ambiguity which Java has introduced.
l
For your use case where you don't want comparability, do you need to iterate on the set elements? If so, do you need a predictable order too? Do you need to persist these values and read them back from a newer version of your program? Do you need to transfer them to another system that could have an updated, or older version with a different set of values?
m
I suggest to look at Swift. There are brilliant developers specifying the language and they've though thought all of it while deeply involving the community for feedback. By default only minimal functionality is given. Enums are just a set of possible values. They can be Comparable (manually defined order), they can be enumerable (via interface which would be implemented automatically). I've also linked above the recent and lengthy debate about forward and backward compatibility of enums including add/remove/rename and exhaustiveness. In the end what makes or doesn't make sense always depends on the use case - what the developer want to express - and in Swift developer is very free to make it just right for their specific cases.
d
I simply disagree that it should be changed or is worth changing
l
Community feedback and being brilliant doesn't always imply simple or consistent solutions. I just read Swift enum documentation, and here's my thoughts: 1. They start by defining their own definition of enumeration, basically pulling it to become a set/group of value, without anything regarding the enumeration apart from the
allCases
property defined later on. 2. To have something like `sealed class`es, they use the keyword
indirect
to have an enum case be another enum, which reads harder than it should be IMHO. 3. Their "enums" are really just a set/group of values that enables genuinely cool syntax in `switch`es and the related compiler checks, and you can make them use any primitive value, be it a
Character
, a
Float
, etc. Consequently, I think we're good in Kotlin with `sealed class`es, Java's and Kotlin enums are more accurate to the English definition of enumeration with their comparability, ordinals and names, and are also simpler although less full featured than Swift's fake enums. I'm not against something like in Swift as kind of a middle ground between `sealed class`es and `enum class`es, but I don't want them to be referred as enumerations. `set class`es is one of the naming I have in mind. If you think such a thing should be introduced in Kotlin because you have or can find use cases for it that current options don't satisfy well, then starting to work on a KEEP would be sound thing to do.
g
kind of a middle ground between `sealed class`es and
enum class
oh, please, no need to add new entities to the language, some people already confused what to choose enum or sealed class
1