Generic function wrappers in Kotlin?

I want to implement a function with takes some function f and returns a new function with the same signature as f, which logs something and then invokes f.

For example, I can write this for a 1-arg (T) -> R function like:

fun <T, R> wrapFunction(f: (T) -> R): (T) -> R {
  return { t ->
    logSomething()
    f(t)
  }
}

Is there some way to implement a generic function that does this for any function passed in?
e.g.

fun <T : Function<R>, R> wrapFunction(f: T): T {
  // Can this be implemented in Kotlin?
}

The best I’ve come up with is to implement all the wrapFunction overloads up to some limit of args taken e.g. 0 args, 1 arg, up to say 10 args. However it’d be nice to even just have a fun <T : Function<R>, R> wrapFunction(f: T): T that can delegate to them so that all the overloads don’t pollute IJ’s auto-complete.

Thanks for reading and for any thoughts!

2 Likes

If you are concerned about cluttering the namespace, then you can move all implementations inside the function and perform runtime checks. This way we keep the whole garbage inside the function. Something like this:

fun <T : Function<*>> wrapFunction(f: T): T {
    return when (f) {
        is Function1<*, *> -> object : Function1<Any?, Any?> {
            override fun invoke(p1: Any?): Any? {
                println("pre-invoke")
                return (f as Function1<Any?, *>).invoke(p1)
            }
        }
        is Function2<*, *, *> -> object : Function2<Any?, Any?, Any?> {
            override fun invoke(p1: Any?, p2: Any?): Any? {
                println("pre-invoke")
                return (f as Function2<Any?, Any?, *>).invoke(p1, p2)
            }
        }
        else -> error("")
    } as T
}

But there is one huge problem with this technique. We want T to be downcast from Function<*> to store the number and types of arguments, but unfortunately we can’t control how deep it will downcast. T may be e.g. KFunction and then we have to create a KFunction. Or it may be any other class implementing Function and then we have to somehow duplicate exactly the same type.

For this reason my above example works with lambdas, but it does not work with function references. I believe we can make it to work with function references, but this is basically hacking and it does not really solve the problem. I think the standard and common solution to this kind of problems in Java/Kotlin is to create one function per the number of arguments. Or use AOP / bytecode manipulation.

2 Likes

Thanks for the reply broot! Makes sense (unfortunately)

1 Like

Decorators are planned I believe in the not-so-far future of Kotlin, and they’ll be able to do just what you’re describing.

1 Like