kotlindslkotlin-dsl

Kotlin DSL variable imitation


Using Kotlin type-safe builders one might end up writing this code

code {
  dict["a"] = "foo"; // dict is a Map hidden inside that can associate some name to some value
  println(dict["a"]); // usage of this value
}

This code is ok, but there is a problem: "a" is just a string. I want it to be like a user-defined variable - an identifier that is recognized by the compiler, auto-complete enabled.

Is there a way to turn it into something like this?

code {
  a = "foo"; // now 'a' is not a Map key, but an identifier recognized by Kotlin as a variable name
  println(a);
}

I can do this if I make code's lambda an extension function over some object with a field a defined inside. This is not what I want. I want to be able to use other variables (with unknown names) as well.

A possible workaround could be

code {
  var a = v("a", "foo");
  println(a);
}

Where v is a method of the extension's object, that stores value "foo" inside "dict" and also returns a handle to this value.

This case is almost perfect, but can it be clearer/better somehow?


Solution

  • You can use property delegation:

    class Code {
        private val dict = mutableMapOf<String, String>()
    
        operator fun String.provideDelegate(
            thisRef: Any?,
            prop: KProperty<*>
        ): MutableMap<String, String> {
            dict[prop.name] = this
            return dict
        }
    }
    

    Use case:

    code {
        var a by "foo" // dict = {a=foo}
        println(a) // foo
        a = "bar" // dict = {a=bar}
        println(a) // bar
    }