1
votes

I'm playing around with Kotlin and I think that it would be great to have "functional classes", see the example of what I want:

functional class MyStrFunction(val function: MyStrFunction.(String) -> String) {
    val memory = HashMap<String, String>()
    fun output(message: String) = println(message)
}

The idea is that I would be able to use it like:

val f = MyStrFunction { 
    output("reversing $it"); 
    if (it in memory)
        output(memory[it])
    return it.reverse() 
}

If I were to write MyStrFunction which I want by myself, the resulting class would be:

class MyStrFunction(val function: MyStrFunction.(String) -> String) {
    val memory = HashMap<String, String>()
    fun output(message: String) = println(message)

    public fun invoke(s: String) = this.function(s)
}

But it's tedious a bit to write this by hands in every "functional class".

So here's my question: is it possible to make functional an annotation and then process it adding invoke method with the desired code?

I know that kapt can be used for Kotlin annotation processing, but AFAIK for now it cannot generate Kotlin code and there's no way to make an extension function in Java generated code.

Is there any way to generate a new Kotlin method for a class?

Node: I understand that the idea of functional classes has few use cases and that it's not that hard to add the mentioned invoke method manually, but for me it's academical interest to the concept of annotation processing in Kotlin.

UPD: Just my thought about functional classes: there's language feature of delegating interfaces and it would work good here, but this reference is not visible in that block. Example:

class MyStrFunction(val function: MyStrFunction.(String) -> String)
: (String) -> String by { this.function(it) } { // <-- won't compile :(
    val memory = HashMap<String, String>()
    fun output(message: String) = println(message)
}
1
You can search or add an issue to YouTrack asking for the feature of KAPT to generate Kotlin. This way you will be able to track progress for the feature.Jayson Minard

1 Answers

3
votes

Not sure, if what you want can be done directly, however here's an approach without "functional classes". As far as I understand you want to add functionality to a regular lambda function. First, define your functionality as a regular class:

class Memory() {
    val memory = hashMapOf<String, String>()
    fun output(message: String) = println(message)
}

Then define a helper function to bind extension functions to a receiver:

fun <T, R, E> T.bind(function: T.(R) -> E): (R) -> E = { this.function(it) }

And then use it like this

fun main(args: Array<String>) {
    val mem = Memory()

    val memoryFunction: Memory.(String) -> String = {
        output("reversing $it");
        if (it in memory)
            output(memory[it])
        it.reverse()
    }

    val function: (String) -> String = mem.bind(memoryFunction)

    println(listOf("foo", "bar").map(function))
}