Is there a way to restrict extension methods in DSLs?
Say I have a class structure like this:
class Outer {
fun middle(op: Middle.() -> Unit): Middle {
val middle = Middle()
middle.op()
return middle
}
}
class Middle {
fun inner(op: Inner.() -> Unit): Inner {
val inner = Inner()
inner.op()
return inner
}
}
class Inner
fun outer(op: Outer.() -> Unit): Outer {
val outer = Outer()
outer.op()
return outer
}
I can then create a call like so:
outer {
middle {
inner {
middle { } // Here is the problem
}
}
}
My problem is that the marked middle { }
call is confusing, as it adds a Middle
to the Outer
when it looks like it is adding to the Inner
.
Is there a way to not allow the middle { }
call?
You can use a workaround with deprecated
:
class Outer {
fun middle(op: Middle.() -> Unit): Middle {...}
@Deprecated("can not be used inside a Outer block", level = DeprecationLevel.ERROR)
fun outer(op: Outer.() -> Unit): Outer = TODO()
}
class Middle {
fun inner(op: Inner.() -> Unit): Inner {...}
@Deprecated("can not be used inside a Middle block", level = DeprecationLevel.ERROR)
fun middle(op: Middle.() -> Unit): Middle = TODO()
}
class Inner {
@Deprecated("can not be used inside a Inner block", level = DeprecationLevel.ERROR)
fun inner(op: Inner.() -> Unit): Inner = TODO()
}
Now the compiler will give you an error, and IDE will not suggest a wrong function in the completion:
outer {
middle {
inner {
middle { } // error
}
}
}
But you really should not do it for big DSLs. It is better to wait for https://youtrack.jetbrains.com/issue/KT-11551 as @KirillRakhman suggested.
Edit: After I fixed my example, it became much smaller. With one dummy function for a class it is not that much of boilerplate after all.