https://kotlinlang.org logo
Title
t

Travis Griggs

12/07/2018, 7:14 PM
Coming from Python/Smalltalk backgrounds, I really like explicit self receivers. Kotlin allows me to continue to be explicit (optionally) using
this
. For the grins of it (because sometimes silly ideas lead to better understanding), I thought I’d try something like this:
val <Receiver>Receiver.self: Receiver get() = this
It works pretty well. I can now use
self
in lieu of
this
. Except in a few cases. One such case is in an
init
block: private val manager:DownloadManager init { self.manager = self.context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager } It will not allow the
self.manager
reference there. It’s OK with the
self.context...
. And I can use just
manager
or
this.manager
, but for some reason my “selfish substitution” does not work there. Enlighten me?
d

diesieben07

12/07/2018, 7:16 PM
Inside an
init
block (and inside a constructor),
this
refers to an uninitialized instance and you are not allowed to let it "escape" (e.g. by calling some function with it).
As for the
self.manager
,
manager
is a
val
and thus must be initialized once and only once in a constructor or init block., The compiler cannot prove that that happens through your
self
indirection
t

Travis Griggs

12/07/2018, 7:18 PM
why then does the
self.context
work there?
a

Alan Evans

12/07/2018, 7:29 PM
Let me get the android crap out that example for you
val <Receiver>Receiver.self: Receiver get() = this

class X {

    val x: Int
    val y: Int = 2

    init {
        self.x = self.y
    }
}
The first
self
is a function. The compiler can't infer that it returns
this
, all it knows is it is an instance of
X
. So it looks to it as though you are attempting to reassign to
x
.
The second
self
is also a function, but there is no issue reading from
x
here (or anywhere).
t

Travis Griggs

12/07/2018, 7:54 PM
so basically,
this
gets special/contextual treatment that allows it to work. but because the
self
adds a level of indirection, it fails? Is that correct(ish)?
Is there a way to make it work?
inline
?
a

Andreas Sinz

12/07/2018, 8:11 PM
IMO
self.y
shouldn't be allowed either, because its possible that
y
isn't initialized at the time
if you want to do it that way, use
lateinit var
instead of
val
, but that sounds like an XY problem
d

Dico

12/08/2018, 4:56 AM
@Andreas Sinz y is initialized before the constructor block because its inline initializer is a part of the class constructor, which always executes top-down. If you move the block above the declaration of y, it will cease to work.
a

Andreas Sinz

12/08/2018, 10:25 AM
it still compiles when
y
is initialized after the constructor block using
self
as an indirection
a

Alan Evans

12/08/2018, 11:41 AM
Same thing applies.
self
is a function. The compiler can't infer that it returns
this
, all it knows is it is an instance of
X
There are even simpler ways to bypass this simple check, local variable:
class X {

    init {
        val x = this
        x.y
    }

    val y: Int = 2
}
This might be why
inline
also has no effect on the problem.
Due to the halting problem, there will always be a way to avoid this check. I.e. you can write a program here with a loop that will complete and will return the same instance as
this
and a human could even be able infer that. But a computer compiler cannot be written to determine the result of a loop (or even if the loop completes) without actually running it. (My oversimplification of the halting problem)
t

Travis Griggs

12/09/2018, 2:35 AM
It’s interesting (to me) that a simple macro system (e.g. #define self this) would make this possible. Not that I’m advocating for the problems a preprocessor system, but it is an interesting irony?
😱 1
a

Alan Evans

12/09/2018, 1:41 PM
To me, macros are so basic that it's kind of obvious they would work. But it is interesting to have examples that show that there is no equivalent in Kotlin.