I find myself building an internal DSL for Kotlin ...
# language-proposals
g
I find myself building an internal DSL for Kotlin that relies on the user creating a large number of extension properties (potentially for types which are parameterized). Over time the verbosity grows quite large and refactors more tedious. My thoughts on the language involve introducing a new block that supports specifying the extension type, declaring type parameters, and providing a scope for declaring properties and functions. All declarations inside the scope would be extensions of the specified type. I see this language construct as a syntactic sugar that would allow the following:
Copy code
interface MyThing<A: Any, B: MyRoot?> { }
 
extension <A: Any, B: MyRoot?> (MyThing<A, B>){
    
    val thingA: A get() = TODO()
    
    val thingB: Optional<B & Any> get() = TODO()
    
    val otherThing: (A) -> String by TODO()
    
    fun <C: SomeType> doThing(): Pair<A, C> {
        return TODO()
    }
}
A reduction step would emit:
Copy code
interface MyThing<A: Any, B: MyRoot?> { }
    
val <A: Any, B: MyRoot?> MyThing<A, B>.thingA: A get() = TODO()
    
val <A: Any, B: MyRoot?> MyThing<A, B>.thingB: Optional<B & Any> get() = TODO()

val <A: Any, B: MyRoot?> MyThing<A, B>.otherThing: (A) -> String by TODO()

fun <A: Any, B: MyRoot?, C: SomeType> MyThing<A, B>.doThing(): Pair<A, C> {
    return TODO()
}
This syntax could also be used inside a class like declaration for member extension functions as well. I could also see more widely applicable usage when allowing something similar for the upcoming context receivers allowing a block for which all declarations require the specified contexts.
5
l
Same here, if we could define a common receiver/context for a block of functions it saves typing and helps grouping a LOT.
g
My suggestion to tweak your syntax is to extend the concept of the existing soft keyword
context
to include your proposal. having sent some time with dotnet I also liked using
this
as a keyword in a signature, so i propose something like:
Copy code
interface MyThing<A: Any, B: MyRoot?> { }

context<A: Any, B: MyRoot>(this: MyThing<A, B>) {
  val thingA: A get() = TODO()
  //...
}
g
So I'm not particularly attached to any syntax as long as it helps achieve a similar level of terse-ness but I'm not sure if reusing context for extension receivers is the best idea. I say that for two reasons: • Firstly, that looks like context receivers, which from my knowledge is something the language isn't actually going to adopt, and instead opt for context parameters. • Secondly, extension receivers and context receivers/parameters are two different concepts from the language perspective in how they are treated (including usage, delegates, and reflection. This distinction is actually something I'm relying on). I think
context
blocks should also be a thing but I think they should just stick to declaring context parameters and not providing a mechanism for extension receivers. I also recognize that neither of my reasons are very rigorous and am open to changing my opinion
I found an issue that relates to this request https://youtrack.jetbrains.com/issue/KT-5670
l
I'm leaning towards the context parameter is a separate feature, while this one is for reducing the efforts on defining many extensions for one long signature.