https://kotlinlang.org logo
#language-proposals
Title
# language-proposals
p

PHondogo

10/16/2023, 11:09 AM
Hello! Consider:
Copy code
class Context {
    val v: Int = 1
}

class X {

    val v: Int = 0

    fun Context.test() {
        println(v) // such cases are difficult for code readers. Suggest to issue warning if not explicitly specified this@... for them 
    }
}
d

dmitriy.novozhilov

10/16/2023, 11:20 AM
Implementation of such warning will dramatically hurts the compilation performance The thing is that when compiler tries to resolve some call/property access it looks for suitable candidates in ordered list of scopes (so called "tower") and stops on first scope in which it finds some suitable candidate It allows to quickly stop the resolution if corresponding property/function was found somewhere in close scope (like in local scope or scope of closest implicit receiver) So to implement this warning compiler should always go through the whole tower, which will be incredibly slow Also what about other cases of such ambiguity? E.g. "local
x
vs member `x`" or "member
x
vs top-level `x`"? Should they be reported too?
BTW context receivers can partially address your issue Scopes of context receivers are located in tower after scope of receivers, so
X.v
will win
Copy code
class Context {
    val v: Int = 1 // (1)
}

class X {
    val v: Int = 2 // (2)

    fun Context.test_2() {
        v // resolved to (1)
    }

    context(Context)
    fun test_2() {
        v // resolved to (2)
    }
}
In presence of context receivers tower looks like this (simplified): • scope of function extension receiver • scope of function dispatch receiver (
this
of parent class) • scopes of context receivers
p

PHondogo

10/16/2023, 11:36 AM
Locals usually visible by reader. For members and globals it is not so good as with locals, especially when members located in super types. But with mix of receivers such cases become less readable. Reader must enter each scope (or ctrl-click) to find actual candidate. In case of such warning he can determine with less effort for what scope this member belongs to.
l

Loney Chou

10/17/2023, 5:48 AM
> ...warning... > and this totally destroys the beauty of DSL.
Copy code
buildJsonObject {
    literal("a", "ObjectBuilder")

    array("b") {
        literal("ArrayBuilder")
    }
}
Real world example speaks more.
p

PHondogo

10/17/2023, 5:51 AM
Could you provide how is 'literal' defined?
l

Loney Chou

10/17/2023, 5:53 AM
One for JsonObjectBuilder with key, one for JsonArrayBuilder without.
A proper
@DslMarker
annotation is all we need.
p

PHondogo

10/17/2023, 5:54 AM
It looks like that this is not the case I'm talking about.
l

Loney Chou

10/17/2023, 5:54 AM
Copy code
class Outer {
    val a: Int

    inner class Inner {
        val a: Int

        fun foo() {
            a // Will you warn?
        }
    }
}
p

PHondogo

10/17/2023, 5:54 AM
In such scenario there will be no warning.
l

Loney Chou

10/17/2023, 5:57 AM
> In presence of context receivers tower looks like this (simplified): > • scope of function extension receiver > • scope of function dispatch receiver (
this
of parent class) > • scopes of context receivers I don't think it's a fault to the language. After someone learns how the resolution happens, it's not a problem anymore. It's consistent.
👍 1
p

PHondogo

10/17/2023, 5:59 AM
It is not the fault, just additional difficulty for readers. No matter about their experiance.
For example i've created class T and use it as function extention receiver for many member functions in different classes. After some time i add function f() to T. After that i can break some logic in any class that have function f() and member with extention receiver T, cause now it will be resolved to T.f(). With warning this will be catched at compile time.
l

Loney Chou

10/17/2023, 6:13 AM
This is costly I suppose.
You'd now have to search for all scopes for all symbols, which I don't think is good for compiler.
p

PHondogo

10/17/2023, 6:18 AM
For readers:
Copy code
class T{
  val x: Int
  fun test Context.() {
    println(x) // while reading code you have to go to Context declaration to check if it has or not x member
  }
}
I thought that compiler is for making programmer life easer.
l

Loney Chou

10/17/2023, 6:21 AM
If you don't know Context, learn it first. If clashes confuse you, all the members from Context will confuse you too because they don't exist in T.
1
p

PHondogo

10/17/2023, 6:25 AM
You can't keep in the head everything. For more complex code you'll spend more time undestanding it.
Especially when working in team.
l

Loney Chou

10/17/2023, 6:26 AM
Then why would you use receiver? Just forbid it and everyone's happy.
p

PHondogo

10/17/2023, 6:30 AM
But i like to use it. With this proposal their usage will be more attractive.
l

Loney Chou

10/17/2023, 6:41 AM
If this is for kotlin IDEA plugin, I could accept, but I don't want my compilation slows down again.
p

PHondogo

10/17/2023, 6:43 AM
I suggest compiler command line flag.