Why does this Minimal, Reproducible Example print ...
# announcements
m
Why does this Minimal, Reproducible Example print
null TheValue
? I expect it to print
TheValue TheValue
Copy code
enum class EnumClass(val self: EnumClass? = null) {
    TheValue(self = TheValue)
}

enum class OtherEnumClass(val other: EnumClass? = null) {
    OtherValue(other = EnumClass.TheValue)
}

fun main() {
    println("${EnumClass.TheValue.self} ${OtherEnumClass.OtherValue.other}")
}
s
You cannot reference TheValue enum before it has been initialized
m
Right, but when I do the printing, everything has been initialized. Technically I don’t see why the code shouldn’t behave as I expect. But I guess the current compiler just doesn’t support this.
d
But the
TheValue
constructor does not run when you print, it runs at the class-init time of
EnumClass
, before
EnumClass.TheValue
has been assigned (how can it be assigned, you are in the middle of creating it's value!)
s
It has nothing to do with the compiler. Yes everything has been initialized when you print but when you call the constructor here:
TheValue(self = TheValue)
TheValue has not been thus self is assigned to null
m
Ok maybe my demands are unreasonable. But then at least a compiler warning or maybe even compiler error would be nice 😬 Anyway, thanks for the input.
s
You’d get your compile-time warnings, if you make the type of the parameter non-nullable. It’s a good idea to try to be non-nullable if possible (e.g. by using
by lazy {..}
to compute values that aren’t available immediately). It pretty much took extra effort (nullable class + default null parameter value) to not make the compiler complain about this 😂
m
Yes the compiler did complain about earlier versions of that code, but after fixing those warnings and not getting new ones, I figured I was good to go 🙂
s
it was good to go. It did exactly what you definded (but not what you wanted 😉) with a self-value that was
null
to start with.
m
exactly what you definded
I don’t see how you can think that
Copy code
TheValue(self = TheValue)
is to be interpreted
Copy code
TheValue(self = null)
s
Not specifically but you always have to be aware of initialization order when running code in the class initializer.
m
I'm surprised the compiler did anything other than stackoverflow on this. You want to assign the value of
self
in a class to the class itself. But in order to assign it, the class has to be constructed. But in order for the class to be constructed, it needs the pointer to the class. etc, etc, etc... So I'm unsure how you expect it to work. And the above also explains why it wouldn't have compiled if
self
was non-null. But just getting it to compile doesn't always guarantee it will work. We all wish it was that simple 😉
m
I expected the compiler to produce the equivalent (not in the literal sense, but I think you get my point) of this code:
Copy code
enum class EnumClass(var self: EnumClass? = null) {
    TheValue();
}

enum class OtherEnumClass(val other: EnumClass? = null) {
    OtherValue(other = EnumClass.TheValue)
}

fun main() {
    EnumClass.TheValue.self = EnumClass.TheValue
    println("${EnumClass.TheValue.self} ${OtherEnumClass.OtherValue.other}")
}
that code runs just fine.
d
But that's not what you told the compiler to do. You told the compiler to read the value of
TheValue
at the point where you construct
TheValue
, in the initializer of
EnumClass
.
☝️ 1
m
I told the compiler to have an enum value field reference itself. That end result is demonstratably possible ☝️
d
That's not how the Kotlin compiler works...
m
Yes my friend, I’ve noticed that 🙂
m
You need to take a step back, and 'think like the machine' when you run the code. In your second example, The
TheValue
object gets created as soon as
EnumClass.TheValue
is executed. This will initialize the object, assign it in memory. And since
self
is nullable AND has a default value, all is good. Now the compiler will execute the assignment of
self
within
TheValue
, and it has an object to point to. This works ONLY because enums are singletons. When you try to combine it, you put the compiler into a stackoverflow situation as described above. Hopefully that helps you conceptualize it.
m
Thanks for taking the time to explain it. I feel (and felt for a while now) that I understand why this is happening. I just don’t think it’s intuitive. But hey, it’s not the end of the world. You adapt your code and move on. I get that the current behavior is defendable.
m
These are the hard parts of programming. Often what seems reasonable to our human mind can't be accomplished (yet) by the compiler. The compiler could achieve internally, what you effectively did with a few lines of code, BUT then we'd complain even more that the compiler is too slow ;(
i
I believe this case of enum entry self-reference should be clearly identifiable by the compiler as an error. I've added this case to the similar issue https://youtrack.jetbrains.com/issue/KT-20662
👍 2