以通用方式向函数添加副作用

Add a side-effect to a function in a generic way

如何编写一个 Kotlin 泛型函数,将函数作为参数并为其添加副作用?例如,

fun something(one: Int, two: String): String { return "${one}, ${two}" }
fun somethingElse(arg: Array<String>): String { return "${arg}" }

val w1 = wrapped(::something)
w1(42, "hello")

val w2 = wrapped(::somethingElse)
w2(arrayOf("ichi", "ni"))

以下适用于仅采用单个参数的函数:

fun <A, R> wrapped(theFun: (a: A) -> R): (a: A) -> R {
    return { a: A ->
        theFun(a).also { println("wrapped: result is $it") }
    }
}

为了使用任意数量的参数进行这项工作,我需要一些构造来为我提供参数列表的类型。不幸的是,不能使用泛型函数,因为它只需要一个参数。以下不编译:

fun <A, R> wrapped(theFun: Function<A, R>): Function<A, R> {
    return { args: A ->
        theFun(*args).also { println("wrapped: result is ${it}") }
    }
}

或者我可以使用 varargs?似乎不适用于 lambda。还是 Kotlin 反射?

使用反射的解决方案:

class KFunctionWithSideEffect<R>(private val f: KFunction<R>, private val sideEffect: (R) -> Unit) : KFunction<R> by f {
    override fun call(vararg args: Any?) = f.call(*args).also { sideEffect(it) }

    override fun callBy(args: Map<KParameter, Any?>) = f.callBy(args).also { sideEffect(it) }
}

fun <R> wrapped(theFun: KFunction<R>, sideEffect: (R) -> Unit = { str -> println("wrapped: result is $str") }) =
    KFunctionWithSideEffect(theFun, sideEffect)

用法:

val w1 = wrapped(::something)
w1.call(42, "hello")

val w2 = wrapped(::somethingElse)
w2.call(arrayOf("ichi", "ni"))