0

Example:

data class T(val flag: Boolean) {
    constructor(n: Int) : this(run {
        // Some computation here...
        <Boolean result>
    })
}

In this example, the custom constructor needs to run some computation in order to determine which value to pass to the primary constructor, but the compiler does not accept the run, citing Cannot access 'run' before superclass constructor has been called, which, if I understand correctly, means instead of interpreting it as the non-extension run (the variant with no object reference in https://kotlinlang.org/docs/reference/scope-functions.html#function-selection), it construes it as a call to this.run (the variant with an object reference in the above table) - which is invalid as the object has not completely instantiated yet.

What can I do in order to let the compiler know I mean the run function which is not an extension method and doesn't take a scope?

Clarification: I am interested in an answer to the question as asked, not in a workaround.

I can think of several workarounds - ways to rewrite this code in a way that works as intended without calling run: extracting the code to a function; rewriting it as a (possibly highly nested) let expression; removing the run and invoking the lambda (with () after it) instead (funnily enough, IntelliJ IDEA tags that as Redundant lambda creation and suggests to Inline the body, which reinstates the non-compiling run). But the question is not how to rewrite this without using run - it's how to make run work in this context.

A good answer should do one of the following things:

  • Explain how to instruct the compiler to call a function rather than an extension method when a name is overloaded, in general; or
  • Explain how to do that specifically for run; or
  • Explain that (and ideally also why) it is not possible to do (ideally with supporting references); or
  • Explain what I got wrong, in case I got something wrong and the whole question is irrelevant (e.g. if my analysis is incorrect, and the problem is something other than the compiler construing the call to run as this.run).

If someone has a neat workaround not mentioned above they're welcome to post it in a comment - not as an answer.

In case it matters: I'm using multi-platform Kotlin 1.4.20.

Tom
  • 4,910
  • 5
  • 33
  • 48
  • Just use the fully-qualified `kotlin.run{ }`? – gidds Dec 17 '20 at 15:15
  • Thanks, @gidds. I'm not sure what the question mark at the end of your comment means, but if it's a question then maybe the answer is that I wasn't aware that there was a `kotlin` package and that some things I took for built-ins - such as the `run` function - are actually just ordinary members of that package. It's all clear now, thanks! – Tom Dec 17 '20 at 22:51
  • I wasn't sure if such a simple answer would meet your stringent criteria :-)  But Kotlin is like Java: you can always refer to any class by its fully-qualified name (i.e. with its position in the package hierarchy).  The standard library (from which the run() function comes) is no different.  And if you're not sure, your IDE will be able to tell you where a function is from. – gidds Dec 17 '20 at 23:27

1 Answers1

2

Kotlin favors the receiver overload if it is in scope. The solution is to use the fully qualified name of the non-receiver function:

kotlin.run { //...

The specification is explained here.

Another option when the overloads are not in the same package is to use import renaming, but that won't work in this case since both run functions are in the same package.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Thanks, @Tenfour04! The specification of how to call things is clear - what wasn't clear to me was that the "built-in" `run` function is just an ordinary member a package named `kotlin`, the existence of which I was not aware of. By the way, I find it a bit ugly to have to write `kotlin,run` (`run` alone is already a bit of an aesthetic compromise, but a more bearable one at that), but it does solve the issue. Thanks again! – Tom Dec 17 '20 at 22:54