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.
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.