Search code examples
kotlingradle-kotlin-dsl

Kotlin DSL - union structure


I am designing a DSL and run into a requirement where I have a variable which could be assigned to different ways. Greatly simplified, I would like to set value property either by an integer or by an expression in String. (The real need is even more complex.)

I would like to write in my DSL:

value = 42

or

value = "6*7"

Behind the scene, the value will be stored in a DynamicValue<Int> structure which contains either an integer or the expression.

class DynamicValue<T>(dv : T?, expr : String) {
    val directValue : T? = dv
    val script : String? = expr
    ...
}

I tried several ways (delegate, class, etc), but none of them provided these syntax.

Is there a way to declare this union like structure?


Solution

  • What do you think about the following syntax:

    value(42)
    value("6*7")
    //or
    value+=42
    value+="6*7"
    

    You can do this with operator functions:

    class DynamicValue<T>() {
        var dv: T? = null
        var expr: String? = null
    
        operator fun invoke(dv : T)  {
            this.dv = dv
            this.expr = null
        }
    
        operator fun invoke(expr: String)  {
            this.dv = null
            this.expr = expr
        }
    
        operator fun plusAssign(dv : T)  {
            this.dv = dv
            this.expr = null
        }
    
        operator fun plusAssign(expr: String)  {
            this.dv = null
            this.expr = expr
        }
    }  
    

    You can't redefine the assign operator in Kotlin, therefor the pure syntax value=42 is not possible.

    But I wouldn't go with operator functions, it's to magical. I would do this:

    val value = DynamicValue<Int>()
    value.simple=42
    value.expr="6*7"
    
    class DynamicValue2<T>() {
        private var _dv: T? = null
        private var _expr: String? = null
        var simple: T?
            get() = _dv
            set(value) {
                _dv = value
                _expr = null
            }
    
        var expr: String?
            get() = _expr
            set(value) {
                _expr = value
                _dv = null
            }
    }