- Author(s): Philippe Hausler
[ Source, Source, Source | Tests, Tests, Tests, ]
Array
, Dictionary
and Set
are some of the most commonly-used data structures for storing collections of elements. Having a way to transition from an AsyncSequence
to a collection is not only a useful shorthand but a powerful way of expressing direct intent for how to consume an AsyncSequence
.
This type of functionality can be useful for examples and testing, and also for interfacing with existing APIs that expect a fully-formed collection before processing it.
Three categories of initializers will be added to provide initializers for those three primary types: Array
, Dictionary
and Set
. However these initializers can be written in a generic fashion such that they can apply to all similar collections.
RangeReplaceableCollection
will gain a new initializer that constructs a collection given an AsyncSequence
. This will allow for creating arrays from asynchronous sequences but also allow for creating types like Data
or ContiguousArray
. Because of the nature of asynchronous sequences, this initializer must be asynchronous and declare that it rethrows errors from the base asynchronous sequence.
extension RangeReplaceableCollection {
public init<Source: AsyncSequence>(
_ source: Source
) async rethrows
where Source.Element == Element
}
Dictionary
will gain a family of new asynchronous, rethrowing initializers to parallel the existing Sequence
-based initializers. The initializers will be asynchronous to facilitate uniquing keys and other tasks that may be asynchronous, in addition to the asynchronous initialization of the dictionaries.
extension Dictionary {
public init<S: AsyncSequence>(
uniqueKeysWithValues keysAndValues: S
) async rethrows
where S.Element == (Key, Value)
public init<S: AsyncSequence>(
_ keysAndValues: S,
uniquingKeysWith combine: (Value, Value) async throws -> Value
) async rethrows
where S.Element == (Key, Value)
public init<S: AsyncSequence>(
grouping values: S,
by keyForValue: (S.Element) async throws -> Key
) async rethrows
where Value == [S.Element]
}
SetAlgebra
will gain a new asynchronous, rethrowing initializer that constructs a SetAlgebra
type given an AsyncSequence
. This will allow for creating sets from asynchronous sequences and allow for creating types like OptionSet
types or IndexSet
.
extension SetAlgebra {
public init<Source: AsyncSequence>(
_ source: Source
) async rethrows
where Source.Element == Element
}
Each of the initializers is intended for uses where the AsyncSequence
being used for initialization is known to be finite. Common uses include:
- Reading from files via the
AsyncBytes
style sequences orlines
accessors. - Gathering elements produced by a
TaskGroup
. - Accessing a prefix of an indefinite
AsyncSequence
.
Each of the initializers will use the for
-await
-in
/for
-try
-await
-in
syntax to iterate the sequence directly in the initializer. In addition each initializer relies on the AsyncSequence
being passed in to properly respect cancellation. In the cases where cancellation is a potential, developers should be ready to either check immediately or be ready for initialization based on a partial sequence, depending on the behavior of the AsyncSequence
being used.
let contents = try await Data(URL(fileURLWithPath: "/tmp/example.bin").resourceBytes)
let table = await Dictionary(uniqueKeysWithValues: zip(keys, values))
let allItems = await Set(items.prefix(10))
The spelling of these initializers could be expressed as a trailing conversion. However, that can lead to hard-to-read chains of operations. Functionally these all belong to the reduce
family of functions, but due to source readability concerns they are more ergonomic for understanding what the code does by using the initializer patterns.
The direct inspiration for each initialization is from their standard library counterparts.