Currently to create a new task group, there are two generics involved: ChildTaskResult
and GroupResult
. The latter can often be inferred, but the former must always be supplied as part of either the withTaskGroup(of:returning:body:)
or withThrowingTaskGroup(of:returning:body:)
function. For example:
let s = withTaskGroup(of: Void.self) { group in
group.addTask { /* ... */ }
group.addTask { /* ... */ }
group.addTask { /* ... */ }
return "Hello, world!"
}
The type of s
(which is the GroupResult
generic) is inferred above. However, the return value of the addTask
closures cannot be inferred and must be supplied via of childTaskResultType: ChildTaskResult.Type
(above as Void
). However, it seems like this can be inferable if the function signatures are updated. Right now, withTaskGroup(of:returning:body:)
looks like:
public func withTaskGroup<ChildTaskResult, GroupResult>(
of childTaskResultType: ChildTaskResult.Type,
returning returnType: GroupResult.Type = GroupResult.self,
body: (inout TaskGroup<ChildTaskResult>) async -> GroupResult
) async -> GroupResult where ChildTaskResult : Sendable
withThrowingTaskGroup(of:returning:body:)
essentially looks the same with regards to the two generics. If this is updated to add a default value for childTaskResultType
like so:
public func withTaskGroup<ChildTaskResult, GroupResult>(
of childTaskResultType: ChildTaskResult.Type = ChildTaskResult.self,
returning returnType: GroupResult.Type = GroupResult.self,
body: (inout TaskGroup<ChildTaskResult>) async -> GroupResult
) async -> GroupResult where ChildTaskResult : Sendable
Then it seems like in most cases the ChildTaskResult
generic can be inferred. I'm curious if there's a reason that this cannot be done: as in, is there something I'm missing or if there is another reason that the API was designed to not have this generic inferable (with the default argument). Reading through SE-0304 Structured Concurrency I couldn't find anything about this API being designed specifically in this way.
It seems like this would be a good improvement for the API overall. The initial (albeit silly) example could become:
let s = withTaskGroup { group in
group.addTask { /* ... */ }
group.addTask { /* ... */ }
group.addTask { /* ... */ }
return "Hello, world!"
}
The withTaskGroup(of:returning:body:)
and withThrowingTaskGroup(of:returning:body:)
APIs are already somewhat confusing, especially for new developers of the APIs, so being able to reduce the need to understand the different generics by having them inferred more often seems like a win.