Can I convert a Kotlin KFunction1 to a KFunction0

2019-08-20 09:18发布

问题:

I have a reference to a functionthat needs a parameter.

fun foo(x: Int) = 2 * x
val f: KFunction1<Int, Int> = ::foo

Is there any way to write applyArgument where

val f2: KFunction0<Int> = f1.applyArgument(42) 
assertEquals("foo", f2.name)
assertEquals(84, f2())

I don't want to use a callable reference, as I need access to the name property.

回答1:

hope it helps you:

fun foo(x: Int) = 2 * x
val f1 = ::foo
val f0 = { -> f1(42) }
f0() //84


回答2:

KFunctions are intented to represent functions that are explicitly decleared in Kotlin code, but f2 is not declared anywhere in the code. In addition KFunction has lot of reflection properties and functions which are not relevant to the applied function f2. Therefore even if it is possible it is not recommended.

If you want to do it anyway you can simply write an applyArgument function in this way:

fun <T, R> KFunction1<T, R>.applyArgument(value: T): KFunction0<R> {
    return object : KFunction<R> by this, KFunction0<R> {
        override fun invoke(): R {
            return this@applyArgument(value)
        }
    }
}

But, if what you need is to preserve the name, I would do it in a safe way. One way could be:

data class Named<out T>(val name: String, val value: T)

fun <T, R> Named<T>.map(transform: (T) -> R): Named<R> = Named(name, transform(value))

val <F : KFunction<*>> F.named: Named<F>
    get() = Named(name, this)

Then use it:

fun foo(x: Int) = 2 * x
val f: Named<(Int) -> Int> = ::foo.named

val f2: Named<() -> Int> = f.map { fValue -> { fValue(42) } }
assertEquals("foo", f2.name)
assertEquals(84, f2.value())


回答3:

Partial application is possible.

You may just declare a function for partial application and use it for the :: reference.

Hence, the name would not be the original function. Another approach - create your own classes/interfaces

data class MyFunction1<T, R>(val name: String, val f: (T) -> R) { 
  operator fun invoke(t: T) = f(t) 
} 

data class MyFunction0<R>(val name: String, val f: () -> R) { 
  operator fun invoke() = f() 
} 

Now define the curring:

fun MyFunction1<T, R>.curry(t: T) = MyFunction0(name){ f(t) } 

(it can be a member function too)