Suspend constructors

From the design document:

Property getters and setters, constructors, and some operators functions (namely getValue, setValue, provideDelegate, get, set, and equals) cannot have suspend modifier. These restrictions may be lifted in the future.

just want to describe possible motivation for the suspend constructors in particular.

I personally very like manual dependency injection. It is fast, IDE and compile time checked, absolutely explicit and lightweight.
This is easy achivable in thread-blocking world, but now i live in the world of reactive APIs and event-loops.

Instead of this:

class Component suspend constructor(
    val dependency1: Component1, 
    val dependency2: Component2
) {
    val prop1 = dependency1.suspendCall()
    val prop2 = dependency2.suspendCall()

    suspend init {
        // ...
    }
}

i have to write this boilerplate every time for every suspend-initializable component in my app:

class Component private constructor(
    val dependency1: Component1, 
    val dependency2: Component2,
    val prop1: String,
    val prop2: String
) {
    private suspend fun init() {
        // ...
    }

    companion object {
        suspend fun create(
            val dependency1: Component1, 
            val dependency2: Component2
        ) {
            val prop1 = dependency1.suspendCall()
            val prop2 = dependency2.suspendCall()
            return Component(dependency1, dependency2, prop1, prop2).also {
                 it.init()
            }
        }
    }
}

Impossibility of inheritance is also very depressing, since not all problems are easily solved with delegation.

As far as I remember all suspend functions must have java.lang.Object as return type in bytecode, and constructors must return void.

@rpuxa you remember wrong.
@wdoker The suspend constructors could be easily emulated by suspended builder functions like fun Component(){} with appropriate suppress for linter or operator fun invoke(){} on companion. It is a preferred way to treat such problems because complicated initialization code for classes could lead to unexpected errors. We discuss this problem a lot in slack and other chats, and I think that the common consensus is to limit use of init blocks wherever it is possible and use builder functions instead.

Ok, I could be wrong. Can you get me a counterexample?

It has nothing to do with bytecode. Constructors do not exist in it. Suspended functions differ from regular ones not by result type, but by implicit argument. It is in theory possible to pass that argument to constructor, but we are not talking about theroretical possibility, but about language feature.

Constructors exist in bytecode and differ from regular ones by this:

  1. They must have “(…)V” descriptor
  2. They can be invoked only with INVOKESPECIAL instruction

And suspend functions

  1. Must have “(…Lkotlin/coroutines/Continuation;)Ljava/lang/Object;” descriptor
  2. Can be invoked only with INVOKEVIRTUAL instruction

Consequently suspend constructors cannot exist in theory

They don’t have to be represented in bytecode as constructors, though, do they? suspend itself is represented in bytecode completely differently from how it’s written, so it’s not like it’d be anything new.

OK, let’s say we all agree that introducing suspend constructors into the language is a bad idea.
The fact that i have to write boilerplate and the language does not provide any tools to remove it.