I'm not certain that this is an overall improvement. Particularly with TaskGroup.addTask, there really is a meaningful distinction between "no preference" (i.e., inherit whatever preference the current task has) and "run on the global concurrency pool". The former is quite naturally expressed as nil, so long as the argument label is something meaningful, e.g., addTask(executorPreference: nil).
Under your proposed amendment, which overload of addTask gets called determines whether you inherit or not. That will work, but I have a few concerns about that being the approach:
- There's value in being able to explicitly write that we're adding a task without an executor preference, e..g, for documentation or auditing purposes. Omission of an argument is a subtle way to do this, whereas the aforementioned
addTask(executorPreference: nil) { } makes it clear that you thought about it and chose not to state a preference. addTask() { } is more subtle.
- The fact that we have two overloads is really an accident of history, because this feature is coming a few years after
addTask was introduced. We wouldn't even have overloads here if not for the availability difference, so it feels odd to rely on them being different.
- If we need to add another addTask overload for some other reason next year (e.g., to add one more optional parameter), we would have to add two new overloads---one with an executor preference, and one without. This doesn't scale well for future evolution.
Now, I can understand the desire to replace the existential any TaskExecutor with an opaque parameter some TaskExecutor: that is generally the direction we tell folks to go, and I've pushed for it as well in a number of places. However, we're not buying ourselves much with this change, because the way this feature is implementation effectively requires that we immediately type-erase this value.
Personally, I think the (any TaskExecutor)? formulation in the original proposal was better, and that the only improvement we need for the parameter is a more descriptive argument label.
I certainly do understand the desire to have a way to spell the "global concurrent pool"; that was missing functionality from the original proposal, and it's useful to be able to say explicitly (especially for the addTask operation of task groups) "run this on the global concurrent pool." In a world where we're passing existentials task executors into addTask, Task.init, and so on, I don't think we need the DefaultConcurrentExecutor type to exist: we only need a global variable:
var globalConcurrentPool: any TaskExecutor
and, yes, I'd like us to consider whether we can make that assignable (with whatever runtime restrictions are needed) now or in the future, so it's possible to drop in your own concurrent pool in at the application level rather than overriding the C++ hooks like one does today.
Doug