Kotlin 中令人困惑的函数类型用法

Confusing function type usage in Kotlin

我正在阅读 kotlin 协程源代码。

查看下面的代码片段:

  public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
        when (this) {
            DEFAULT -> block.startCoroutineCancellable(receiver, completion)
            ATOMIC -> block.startCoroutine(receiver, completion)
            UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
            LAZY -> Unit // will start lazily
        }

在CoroutineStart.kt

  internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) =
    runSafely(completion) {
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
    }

在Cancellable.kt

我的困惑是:

block 作为类型为 R.() -> T 的函数, 它如何调用 startCoroutineCancellable 哪个函数类型 (R) -> T

这在语言规范的“Function Types”部分进行了解释(强调我的)。

A function type with receive FTR

FTR(RT, A1, A2, ... An) -> R

[...]

From the type system’s point of view, it is equivalent to the following function type

FTR(RT, A1, A2, ... An) -> R ≡ FT(RT, A1, A2, ... An) -> R

i.e. receiver is considered as yet another argument of its function type.

[...] for example, these two types are equivalent w.r.t. type system

Int.(Int) -> String
(Int, Int) -> String

因此 R.() -> T 的实例完全可以用作 (R) -> T 上扩展的接收者,反之亦然,因为类型系统将它们视为同一事物。

请注意,这两种类型在重载决议的上下文中是不同的。例如,如果你说 someInt.bar(),并且作用域中有两个 bar,一个类型为 Int.() -> Unit,另一个类型为 (Int) -> Unit,那么重载解析将知道这些是不同类型的函数,选择前一个。

旁注:startCoroutineCancellableinvoke 中使用的函数类型在技术上是 suspending function types,但上面所说的仍然适用。

R.() -> T(R) -> T 从调用者的角度来看是非常相似的函数类型。他们只收到 R 和 return T。他们的主要区别在于 body 的视角。看这个例子:

val f1: Int.() -> String = { toString() }
val f2: (Int) -> String = f1
val f3: Int.() -> String = f2

如我们所见,我们可以在两种类型之间自由转换,即使没有显式转换。

一种简洁明了的表达方式是,使用接收者定义的函数类型与将接收者作为第一个参数移入参数的函数类型具有相同的签名。它们几乎在任何地方都可以互换,除非使用 lambdas 来表示它们,以及调用它们时。您可以调用带有接收器的函数,并将接收器转置为第一个参数(反之亦然,在其第一个参数的实例上调用无接收器函数)。