Hi folks, not sure if my question is for this chan...
# codingconventions
g
Hi folks, not sure if my question is for this channel so feel free to point me to the right one 🙂 . I am browsing some code from kotlinx.coroutines and kotlin.serialization libraries and i have observed that the jet-team rarely-ish uses space between code, for example: to group different bs-logics., etc etc. Anyone knows the whys?, feel free to point me to books, docs, blogs etc and thanks for any answers in advance !
Is it the recommended way to write code?, Maybe it is because they already have some comments in-between so there is no need for spaces?. When i code i usually try to group my code and separate it with spaces.
m
By spaces you mean throwing a bunch of line breaks between groups of, say, functions?
g
My bad, did not specify that, I mean inside functions, no between functions. Random Example:
Copy code
override fun trySelectOther(otherOp: PrepareOp?): Any? {
    _state.loop { state -> // lock-free loop on state
        when {
            // Found initial state (not selected yet) -- try to make it selected
            state === NOT_SELECTED -> {
                if (otherOp == null) {
                    // regular trySelect -- just mark as select
                    if (!_state.compareAndSet(NOT_SELECTED, null)) return@loop
                } else {
                    // Rendezvous with another select instance -- install PairSelectOp
                    val pairSelectOp = PairSelectOp(otherOp)
                    if (!_state.compareAndSet(NOT_SELECTED, pairSelectOp)) return@loop
                    val decision = pairSelectOp.perform(this)
                    if (decision !== null) return decision
                }
                doAfterSelect()
                return RESUME_TOKEN
            }
            state is OpDescriptor -> { // state is either AtomicSelectOp or PairSelectOp
                // Found descriptor of ongoing operation while working in the context of other select operation
                if (otherOp != null) {
                    val otherAtomicOp = otherOp.atomicOp
                    when {
                        // It is the same select instance
                        otherAtomicOp is AtomicSelectOp && otherAtomicOp.impl === this -> {
                            /*
                             * We cannot do state.perform(this) here and "help" it since it is the same
                             * select and we'll get StackOverflowError.
                             * See <https://github.com/Kotlin/kotlinx.coroutines/issues/1411>
                             * We cannot support this because select { ... } is an expression and its clauses
                             * have a result that shall be returned from the select.
                             */
                            error("Cannot use matching select clauses on the same object")
                        }
                        // The other select (that is trying to proceed) had started earlier
                        otherAtomicOp.isEarlierThan(state) -> {
                            /**
                             * Abort to prevent deadlock by returning a failure to it.
                             * See <https://github.com/Kotlin/kotlinx.coroutines/issues/504>
                             * The other select operation will receive a failure and will restart itself with a
                             * larger sequence number. This guarantees obstruction-freedom of this algorithm.
                             */
                            return RETRY_ATOMIC
                        }
                    }
                }
                // Otherwise (not a special descriptor)
                state.perform(this) // help it
            }
            // otherwise -- already selected
            otherOp == null -> return null // already selected
            state === otherOp.desc -> return RESUME_TOKEN // was selected with this marker
            else -> return null // selected with different marker
        }
    }
}
While i code mostly i try to group my logic for example:
Copy code
override suspend fun replaceMasterDevice(input: NewMasterDevice, client: Client): HubPayload<Int> {
    if (input.isNotRegistrationIdValid()) return badRequest()

    val masterDevice = input.toDevice()
    val registrationId = masterDevice.registrationId
    val currentMasterDevice = staleDeviceServiceRepoService.getCurrentMasterDevice(masterDevice.accountUuid)
        ?: return notRegisteredError(input.accountUuid)

    // maybe return something more specific here?
    if (currentMasterDevice.registrationId.compareTo(registrationId) == 0) return badRequest()
    val server = serverRepoService.getServer(client)
    val newMasterDevice = masterDevice.toDeviceEntity(MASTER_DEVICE_ID, server.id, true)
    
    val newStaleDeviceId = staleDeviceServiceRepoService.replaceMasterDevice(newMasterDevice)
    return dataPayload(newStaleDeviceId)
}
This could have been better if it was with no break lines?
Copy code
override suspend fun replaceMasterDevice(input: NewMasterDevice, client: Client): HubPayload<Int> {
    if (input.isNotRegistrationIdValid()) return badRequest()
    val masterDevice = input.toDevice()
    val registrationId = masterDevice.registrationId
    val currentMasterDevice = staleDeviceServiceRepoService.getCurrentMasterDevice(masterDevice.accountUuid)
        ?: return notRegisteredError(input.accountUuid)
    // maybe return something more specific here?
    if (currentMasterDevice.registrationId.compareTo(registrationId) == 0) return badRequest()
    val server = serverRepoService.getServer(client)
    val newMasterDevice = masterDevice.toDeviceEntity(MASTER_DEVICE_ID, server.id, true)
    val newStaleDeviceId = staleDeviceServiceRepoService.replaceMasterDevice(newMasterDevice)
    return dataPayload(newStaleDeviceId)
}
m
So in the second example I'd put breaks too, but in the first for me the control flow blocks and comments make up for enough spacing.
g
Another example from coroutine lib:
Copy code
private fun ensureCapacity() {
    val currentSize = elements.size
    val newCapacity = currentSize shl 1
    val newElements = arrayOfNulls<Any>(newCapacity)
    elements.copyInto(
        destination = newElements,
        startIndex = head
    )
    elements.copyInto(
        destination = newElements,
        destinationOffset = elements.size - head,
        endIndex = head
    )
    elements = newElements
    head = 0
    tail = currentSize
}
If it were me i would put some spaces after the vals at least (maybe?) i guess there is some thought behind it or i am reading into it too much?