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
CodePudding user response:
R.() -> T
and (R) -> T
are very similar function types from the caller perspective. They just receive R
and return T
. They differ mostly from the perspective of their body. See this example:
val f1: Int.() -> String = { toString() }
val f2: (Int) -> String = f1
val f3: Int.() -> String = f2
As we can see, we can convert between both types freely, even without an explicit cast.
CodePudding user response:
This is explained in the "Function Types" section of the language spec (emphasis mine).
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
So an instance of R.() -> T
can totally be used as the receiver of an extension on (R) -> T
and 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 two bar
s in scope, one with type Int.() -> Unit
and 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 startCoroutineCancellable
and invoke
are technically suspending function types, but what is said above still applies.