samedi 13 février 2021

How dynamically combine interfaces in Kotlin?

I am not sure how to word the question in a more correct fashion or how to come up with an example that is less contrived.

Let's say that I have an interface that describes some fields (Data in my example).

I then want to apply some transformations to it (sum and multiply the fields in my example). Ideally, this should be like a data pipeline configurable at runtime by the user of the application (via a config file, for example). I also define interfaces for properties that get added to my object by each of the transformations (SummedData and MultipliedData).

I also define some classes (DataSummer and DataMultiplier) that implement these interfaces as well as the original Data interface, so they can be "cascaded" and applied one after the other. In my example, these are very simple, but you can imagine them lazily calling into external APIs or doing some expensive processing.

Everything works, except each subsequent transformation removes the type information of the previous one (in my example, data is SummedData is always true and data is MultipliedData is always false despite them both being applied to the original object). What I want is both of the checks to be true no matter the order I apply the transformations.

I have an idea of how to do this in a more dynamic language like Python, but I wonder if something similar can be done with Kotlin.

Code example:

interface Data {
    val field1 : Int
    val field2 : Int
}

interface SummedData  {
    val fSum : Int
}

interface MultipliedData {
    val fProd : Int
}

data class DataSummer(private val iData : Data, private val iSum : SummedData) : Data by iData, SummedData by iSum{
    constructor(data : Data) : this(
        data,
        object:SummedData {override val fSum:Int = data.field1 + data.field2}
    )
}

data class DataMultiplier(private val iData : Data, private val iMul : MultipliedData) : Data by iData, MultipliedData by iMul{
    constructor(data : Data) : this(
        data,
        object:MultipliedData {override val fProd:Int = data.field1 * data.field2}
    )
}

fun main() {
    var data:Data = object : Data {
        override val field1 = 2
        override val field2 = 3}

    data = DataMultiplier(data)
    data = DataSummer(data)

    //Always false :(
    if (data is MultipliedData){
        println("Product of ${data.field1} and ${data.field2} is ${data.fProd}" )
    }
    if (data is SummedData){
        println("Sum of ${data.field1} and ${data.field2} is ${data.fSum}" )
    }
}

Thanks in advance for the answer!

Aucun commentaire:

Enregistrer un commentaire