MarioAriasC / funKTionale

Functional constructs for Kotlin
913 stars 71 forks source link

Question regarding memoization of member functions #32

Closed Petikoch closed 7 years ago

Petikoch commented 7 years ago

Hi Mario,

thank you very much for funKTionale!

I have a question regarding memoization of member functions. I'm used to Java and Groovy and a Kotlin Newbie.

Maybe you have some time and can give some insights on this...

I'd like to memoize the sum member function of this immutable data class (just a silly example)

package tryout

data class MyDataClass(val a: Int = 1,
                       val b: Int = 2) {

    fun sum(): Int {
        println("Calculating...")
        return a + b
    }
}

fun main(args: Array<String>) {
    val myData = MyDataClass()
    println(myData.sum())
    println(myData.sum())
    println(myData.sum())
}

In groovy I can do this using an annotation

package tryout

import groovy.transform.CompileStatic
import groovy.transform.Immutable
import groovy.transform.Memoized

@CompileStatic
@Immutable
class MyGroovyDataClass {

    int a, b

    @Memoized
    int sum() {
        println("Calculating...")
        a + b
    }

    static void main(String[] args) {
        def myData = new MyGroovyDataClass(1, 2)
        println(myData.sum())
        println(myData.sum())
        println(myData.sum())
    }
}

If I want to do this using funkTionale, then there is at the moment "only" this solution, right?

package tryout

import org.funktionale.memoization.memoize

data class MyMemoizedDataClassV1(val a: Int = 1,
                                 val b: Int = 2) {

    private val memoizedSum: (Int, Int) -> Int = { i: Int, j: Int ->
        println("Calculating...")
        i + j
    }.memoize()

    fun sum(): Int {
        return memoizedSum(a, b)
    }
}

fun main(args: Array<String>) {
    val myData = MyMemoizedDataClassV1()
    println(myData.sum())
    println(myData.sum())
    println(myData.sum())

    val myData2 = myData.copy(b = 99)
    println(myData2.sum())
    println(myData2.sum())
    println(myData2.sum())

    val myData3 = myData2.copy(a = 101)
    println(myData3.sum())
    println(myData3.sum())
    println(myData3.sum())
}

Or is there a "more direct" solution?

Thanks for your time & best regards, Peti

Petikoch commented 7 years ago

The example is actually a bit over-simplied and could be solved easily using

data class MyDataClass(val a: Int = 1,
                       val b: Int = 2) {
    val sum = a + b
}

or

data class MyDataClass(val a: Int = 1,
                        val b: Int = 2) {
    val sum: Int by lazy {
        a + b
    }
}

Please ignore this and think of a member function which takes 1..n parameters.

MarioAriasC commented 7 years ago

Hi, thank you for your comments.

You could use functions/methods references


fun sum(a: Int, b: Int) = a + b

val memoizedSum = ::sum.memoize()

For 1..n the solution is a lot less elegant


fun multi(vararg i: Int) = "n"

val memoizeMulti: (IntArray) -> String = ::multi.memoize()

memoizeMulti(intArrayOf(1, 2, 3)) 
Petikoch commented 7 years ago

Hi Mario,

thanks for your answer... I just learned a lot also thru https://stackoverflow.com/questions/44965446/memoization-of-member-function-of-kotlin-data-class, especially about "function properties"...

So, we can even write if we want to have just one memoized "thing"

    val sum = {
        println("Calculating...")
        a + b
    }.memoize()

or with 1..n parameters

    // 1 param
    val sumAndAdd = { addMe: Int ->
        println("Calculating...")
        a + b + addMe
    }.memoize()

    //2 params
    val sumAndAddAndMultiply = { addMe: Int, multiplyBy: Int ->
        println("Calculating...")
        (a + b + addMe) * multiplyBy
    }.memoize()

    ...

Maybe this is worth an entry in the Wiki?

Best regards, Peti

MarioAriasC commented 7 years ago

Feel free to contribute