In other words, in URI.foo(), println(scheme) is equvalent to println(this@foo.scheme), but println(toString()) is equvalent to println(this.toString())
I would expect that to:
1: Print
https
https://example.com/some/path
because the receiver is the URI the extension function is being called on. I.e., as if the code was:
suspend fun URI.foo() = withContext(Dispatchers.IO) {
println(this@foo.scheme)
println(this@foo.toString())
}
Or
2: Be a compilation error, because this inside the withContext block refers to the coroutine, and the coroutine doesnāt have a scheme property. I.e., as if the code was:
suspend fun URI.foo() = withContext(Dispatchers.IO) {
println(this.scheme)
println(this.toString())
}
(which fails to compile with the error āUnresolved reference āschemeāā)
suspend fun <T> withContext(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T
so the receiver is a CoroutineScope.
To access an outer receiver, you can refer to it by the function name this@foo. You can also use a function like so:
fun <T> T.receiver(): T = this
to be called like receiver<URI>() so that you can do it by type
This behaviour exists in Java (I believe) with inner classes.
Consider the following code:
class Foo {
fun foo() {}
fun bar() {}
inner class Bar {
fun bar() {}
fun baz() {
foo() // uses this@Foo
bar() // uses this
}
}
The point is that lexical scoping tells you what declarations are accessible.
An extension function then acts semantically as-if it was magically written inside the class (but itās always final, has no access to private or protected declarations beyond what a non-extension function has in the same scope, etc).
It thus makes sense that multiple receivers would have their declarations accessible.
You can call methods of every available implicit receiver inside a lambda
Thereās probably a more helpful docs page about this, I just couldnāt find it quickly.
EDIT: Hereās another clarification about implicit receivers:
You can declare extensions for one class inside another. Extensions like this have multiple implicit receivers. An implicit receiver is an object whose members you can access without qualifying them with this:
The class where you declare the extension is the dispatch receiver.
The extension functionās receiver type is the extension receiver.
Thereās also this, specifically about extension lambdas (but you might need to squint a little to see this as saying that the implicit receiver is available even to nested extension lambdas):
Function types with receiver, such as A.(B) -> C, can be instantiated with a special form of function literals ā function literals with receiver.
As mentioned above, Kotlin provides the ability to call an instance of a function type with receiver while providing the receiver object.
Inside the body of the function literal, the receiver object passed to a call becomes an implicit this, so that you can access the members of that receiver object without any additional qualifiers, or access the receiver object using a this expression.
This behavior is similar to that of extension functions, which also allow you to access the members of the receiver object inside the function body.
This is an intended feature which powers many (most?) DSLs. If a compiler warning is introduced, many things will break or have spurious warnings.
Context parameters will be stable very soon and do not have this ambiguity. However, replacing extension functions by context parameters is a breaking change, so it wonāt happen for existing methods like `withContext`.