I'm reading the kotlin coroutine source code.
see the code snaps below:
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
}
in CoroutineStart.kt
and
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)
}
In Cancellable.kt
What my confusion is:
block as a function which type is R.() -> T,
how can it invoke startCoroutineCancellable which function type of (R) -> T
This is explained in the "Function Types" section of the language spec (emphasis mine).
So an instance of
R.() -> Tcan totally be used as the receiver of an extension on(R) -> Tand vice versa, because the type system sees them as the same thing.Do note that these two types are not the same in the context of overload resolution. For example, if you say
someInt.bar(), and there are twobars in scope, one with typeInt.() -> Unitand the other with type(Int) -> Unit, then overload resolution will know that these are functions with different types and pick the former one.Side note: the function types used in
startCoroutineCancellableandinvokeare technically suspending function types, but what is said above still applies.