I have a class hierarchy that represents an AST an...
# announcements
t
I have a class hierarchy that represents an AST and I would like to model it with sealed classes to get exhaustion checks in when-expressions. Unfortunately that means I have to put all classes into a single file (which would be several thousand lines long). My idea was to make them all completely abstract (an interface basically) and then move the implementation to Impl classes. The problem with that is that I would no longer be able to inherit implementations (since Kotlin only supports single-inheritance). Is there any more elegant solution than to delegate all the inherited methods manually? Or should I give up on sealed classes completely and use a visitor instead? (I hate visitors but they do have the advantage that client code would still compile when new elements are added to the AST. Although you could achieve the same thing by adding an else-clause to a when-expression despite exhaustion and when-expressions are much more natural to use)
c
You might give each node class an enum “type”, represented as a
val
in the class body (so it can’t be changed in a constructor), and do the
when
checks on that node type instead of the node class itself
t
That seems really awkward too, but still better than a visitor I guess. I'd like to use the type system fully though, i.e. sealed classes.
k
I asked about this a while ago, looks like it just isn't possible: https://kotlinlang.slack.com/archives/C0922A726/p1541610666091700
In fact I was doing an AST too simple smile
t
Yeah, I don't understand why they won't just make sealed classes possible over whole modules. The compiler always sees the whole module so it should be possible. I heard about the trick with
@JvmMultifileClass
as well, but I'm fairly confident it's bullshit. If you look at the generated bytecode,
@JvmMultifileClass
doesn't work like that, it only affects top-level constructs like functions, properties and type aliases that normally can't be top-level in Java. Classes are always independent and know nothing about their Kotlin file. It's not like I can't achieve the same with abstract leaf-classes in my sealed hierarchy and lots of
Impl
classes, it just sucks majorly.
In fact I was doing an AST too
hopefully not the same thing I'm working on 😏
Or alternatively I have to live with huge files. The question is which one is the lesser evil
k
I think I just ended up with large files, it helps a bit to use the "structure" menu on the left in Intelij.
t
Maybe it would be possible to make an annotation processor that generates the sealed class hierarchy and then delegates every method to the non-sealed implementations
I also need to write serializers for all my classes so it may be better to use the "interface" approach rather than one big file (since I would need to override essentially everything)
k
I did think about annotation preprocessors, but kapt isn't that powerful, you can't actually edit the code so it was going to be messy.
t
Do you need to? I assume you could just make a regular class hierarchy that is internal, annotate every class and then generate a completely separate but identical, public sealed class hierarchy that simply delegates everything to your annotated classes.
k
But then you have duplicate class names or something like that?
t
It could look like this:
Copy code
@MultifileSealed(suffix = "Impl)
internal class MySealedLeafImpl() : MySealedBaseImpl() {
    fun foo() {}
}
and the processor generates another class:
Copy code
class MySealedLeaf : MySealedBase() {
    private val impl = MySealedLeafImpl()

    fun foo() = impl.foo()
}
You'd have two classes with similar names for every class you want to have. But that's a small price to pay for practically free multifile sealed classes and you could even try to isolate the impl classes by making them internal in a different module
k
Ah nice, that would indeed work.
t
I'll make this as soon as my current project is ready (since I need it for annotation processing of Kotlin code)
@karelpeeters But there is one big problem I just thought of: Apt/Kapt can't inspect comments, so you can't copy the doc comments