Skip to content

Conversation

dkhalanskyjb
Copy link
Collaborator

Use case: collecting elements up until the point the channel is closed without losing the elements when toList when the exception is thrown.

This function is similar to Flow<T>.toList(destination), which we already have, so the addition makes sense from the point of view of consistency as well.

Use case: collecting elements up until the point the channel is
closed without losing the elements when `toList` when the exception
is thrown.

This function is similar to `Flow<T>.toList(destination)`,
which we already have, so the addition makes sense from the
point of view of consistency as well.
@dkhalanskyjb dkhalanskyjb requested a review from murfel September 10, 2025 08:16
Copy link
Contributor

@murfel murfel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re the original toList() docmuentation - shall it say that toList must be used by a single consumer (the implication of consumeEach and the iterator used there)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It already says so:

  • The operation is terminal.
  • This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the UX is bad and in its current form people will use it incorrectly with a higher probability.

Terminal doesn't redirect anywhere.

Oh wait, you can technically call it from different coroutines, it just doesn't make much sense. Nevermind then.

@dkhalanskyjb dkhalanskyjb force-pushed the dkhalanskyjb/Channel.toList(destination) branch from 63b1e1a to 2d8f52b Compare September 10, 2025 10:49
add(it)
}
}
public suspend inline fun <T> ReceiveChannel<T>.toList(destination: MutableList<T> = ArrayList()): List<T> =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, we can't check that the return value will be used now. Maybe unsplit it back, and do not return List on one of the destination overload?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've attempted this, but cross-referencing overloads clutters the documentation quite a bit, and also, Flow.toList already follows the single-overload approach: public suspend fun <T> Flow<T>.toList(destination: MutableList<T> = ArrayList()): List<T>, so it's more internally consistent to have a single overload.

I'm wondering how real the risk of calling toList() on a channel and forgetting to obtain the resulting list is. Do you have an opinion on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could in theory check it on flow if people do this mistake...

However, having the return value also allows chaining, and that tips the scale for me.

So let's keep your current version.

Copy link
Contributor

@murfel murfel Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if people mistakenly do flow.toList() instead of flow.collect() to trigger flow execution for its side effects.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sandwwraith, what do you think, is it more idiomatic to have fun X.toList(): List<T> + fun X.toList(destination: MutableList<T>): Unit or just fun X.toList(destination: MutableList<T> = ArrayList<T>): List<T> from the point of view of having an excessive return value?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to have it as in the stdlib now: fun X.toList(): List<T> + @Ignorable fun <C: Collection/MutableList> X.toCollection/MutableList(destination: C): C. Generic argument in the second function allows you to collect result to ArrayList or HashSet, for example.

}

@Deprecated("Preserving binary compatibility, was stable", level = DeprecationLevel.HIDDEN)
public suspend fun <T> ReceiveChannel<T>.toList(): List<T> = toList(ArrayList())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline police

@dkhalanskyjb dkhalanskyjb force-pushed the dkhalanskyjb/Channel.toList(destination) branch from 4992f06 to aa1adf1 Compare September 10, 2025 11:04
@dkhalanskyjb dkhalanskyjb force-pushed the dkhalanskyjb/Channel.toList(destination) branch from aa1adf1 to 6039cf8 Compare September 10, 2025 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants