Can I convert a Kotlin KFunction1 to a KFunction0

2019-08-20 09:34发布

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.

3条回答
贪生不怕死
2楼-- · 2019-08-20 09:41

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)

查看更多
祖国的老花朵
3楼-- · 2019-08-20 09:48

hope it helps you:

fun foo(x: Int) = 2 * x
val f1 = ::foo
val f0 = { -> f1(42) }
f0() //84
查看更多
Luminary・发光体
4楼-- · 2019-08-20 09:51

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())
查看更多
登录 后发表回答