-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update the documentation for the Channel interface #4241
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While reading through the updated documentation, I noticed that it doesn't clarify how onUndeliveredElement
and BufferOverflow.DROP_OLDEST
/BufferOverflow.DROP_LATEST
interact (i.e. do dropped elements get passed to the onUndeliveredElement
function). It is only briefly mentioned in the example of the Channel
factory function ("The dropped resources were closed by the channel itself").
I also think the behavior of SendChannel
and ReceiveChannel
functions when the channel was cancelled could be described in more detail. Instead of only mentioning in the documentation of ReceiveChannel.cancel
that other functions will throw CancellationException
s, this could be clarified in the documentation of each function this applies to.
* Returns `true` if either the sending side of this channel was [closed][SendChannel.close] | ||
* and all previously sent items were already received (which also happens for [cancelled][cancel] channels). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence seems to miss an "or" (or is off in some other way).
@@ -200,107 +393,302 @@ public interface ReceiveChannel<out E> { | |||
public val isClosedForReceive: Boolean | |||
|
|||
/** | |||
* Returns `true` if the channel is empty (contains no elements), which means that an attempt to [receive] will suspend. | |||
* This function returns `false` if the channel [is closed for `receive`][isClosedForReceive]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly mentioning the false
for channels that are closed for receive
was clearer to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, it's the opposite, because the second sentence used to contradict the first one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, it's very subjective :)
* If a channel was [closed][close] before [send] was called and no cause was specified, | ||
* an [ClosedSendChannelException] will be thrown from [send]. | ||
* If a channel was [closed][close] with a cause before [send] was called, | ||
* then [send] will rethrow the exact object that was passed to [close]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* then [send] will rethrow the exact object that was passed to [close]. | |
* then [send] will rethrow the exact exception that was passed to [close]. |
ReceiveChannel.receive
uses "exception" instead of "object" and i think that's clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to evoke the feeling that the same runtime object (not just an exception with the same class etc.) was going to be thrown. Reworded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, "exception" is much clearer to me
* If the operation [failed][isFailure] because the channel was closed for that operation, [isClosed] returns `true`. | ||
* The opposite is also true: if [isClosed] returns `true`, then the channel is closed for that operation | ||
* ([ReceiveChannel.isClosedForReceive] or [SendChannel.isClosedForSend]). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be simpler to say something like "... if and only if ..." (stating the logical equivalence and not the implication in both directions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Math-style succinctness is not a good friend when designing APIs or writing documentation for general use. As an example, look at how vacuous truth caused a confusion: https://www.reddit.com/r/Kotlin/comments/q2llvo/what_should_a_function_called/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I didn't think about that.
*/ | ||
public fun getOrThrow(): T { | ||
@Suppress("UNCHECKED_CAST") | ||
if (holder !is Failed) return holder as T | ||
if (holder is Closed && holder.cause != null) throw holder.cause | ||
error("Trying to call 'getOrThrow' on a failed channel result: $holder") | ||
error("Trying to call 'getOrThrow' on a channel closed without a cause") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception might also be thrown when the result represents a failed operation on a channel that isn't closed yet. So the new exception message is a bit misleading.
The term is still used here: |
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
Semantically, |
@@ -200,107 +393,302 @@ public interface ReceiveChannel<out E> { | |||
public val isClosedForReceive: Boolean | |||
|
|||
/** | |||
* Returns `true` if the channel is empty (contains no elements), which means that an attempt to [receive] will suspend. | |||
* This function returns `false` if the channel [is closed for `receive`][isClosedForReceive]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, it's very subjective :)
* If the operation [failed][isFailure] because the channel was closed for that operation, [isClosed] returns `true`. | ||
* The opposite is also true: if [isClosed] returns `true`, then the channel is closed for that operation | ||
* ([ReceiveChannel.isClosedForReceive] or [SendChannel.isClosedForSend]). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I didn't think about that.
Oh, I didn't think about it that way (probably because |
Co-authored-by: Luca Kellermann <[email protected]>
Co-authored-by: Luca Kellermann <[email protected]>
I think this could be clarified. When trying to read from a closed channel it doesn't throw a |
@kjnsn, close the channel with a import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() {
runBlocking {
val c = Channel<Int>()
c.close(CancellationException())
println(runCatching { c.receive() }) // CancellationException
}
} Runnable version; https://pl.kotl.in/vutYqCO0B |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Superb revamp 👍
I have a few minor comments and it will be good to go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
Note: a major piece of terminology that's changed in this is that when a channel is closed with a cause, it's no longer called a "failed channel". I found it immensely confusing to have
channelResult.isClosed
that means "the channel is closed", butchannelResult.isFailed
that doesn't mean "the channel is failed". Also, every cancelled channel is a "failed" channel by the definition of being closed with a cause, which doesn't seem to be the intention. So, I suggest that we remove mentions of "failed channels" throughout and replace them with "closed with a cause". Luckily, it's only relevant in a limited number of places.