so im making some bad decisions to create a DSL: ...
# random
g
so im making some bad decisions to create a DSL: what I wanted was basically intelliJ to give me a kind of folder-path style autocomplete, where i could write something like
Copy code
val domainRelevantKey = Root / HighDomainTerm / MediumDomainTerm / LowDomtainTerm("runtime value!") / OptionallyAFeature
which would be a kind of key for use in our application. As you can imagine by its pathy-ness this kind've imposes a hierarchy on my app, which is what im looking for. I did get this... with a boatload of pretty strange code: https://gist.github.com/Groostav/68234f5ab6d5582b182990c16b548db8 Some pros: - use of static symbols like this means spelling mistakes become compiler errors (where if I'd used some more dynamic things involving strings, I'd likely see misbehaviors that given my usage would be difficult to find in tests) - its likely going to be pretty readable; anybody reading
val relevantSection = Root / Model / ...
is going to understand its usage. - its reasonably extensible in as far as java-with-boilerplate is extensible, in that you can copy-paste-change and have it likely work for your purposes. some cons: - nobody will ever understand my inheritance scheme, I'm pretty sure I wont in 3 months. - each new symbol/domain concept requires more code. A purely dynamic solution wouldn't have this problem --except of course at callsites. - seems pretty overbuilt
to expand on my statement, this works now with
import Level1.*
,
import Level2.*
, etc. but if we had context-sensitive resolution, the compiler could pick those up without additional namespace pollution
Copy code
interface PathSegment<Child>
sealed interface Path<Last : PathSegment<*>> {
    val segments: List<PathSegment<*>>
}
data class Nested<Last : PathSegment<*>>(val parent: Path<*>, val child: Last) : Path<Last> {
    override val segments: List<PathSegment<*>>
        get() = parent.segments + child
}
object Root : Path<Root>, PathSegment<Level1> {
    override val segments: List<Nothing>
        get() = emptyList()
}
enum class Level1 : PathSegment<Level2> {
    HighDomainTerm, // ...
}
enum class Level2 : PathSegment<Level3> {
    MediumDomainTerm, // ...
}
sealed class Level3 : PathSegment<Level4> {
    data class LowDomainTerm(val string: String) : Level3()
    // ...
}
enum class Level4 : PathSegment<Nothing> {
    OptionallyAFeature, // ...
}
operator fun <Last : PathSegment<Child>, Child : PathSegment<*>> Path<Last>.div(child: Child): Path<Child> = Nested(this, child)

(Root / HighDomainTerm / MediumDomainTerm / LowDomainTerm("runtime value!") / OptionallyAFeature).segments ==
    listOf(Level1.HighDomainTerm, Level2.MediumDomainTerm, Level3.LowDomainTerm("runtime value!"), Level4.OptionallyAFeature)