# SE-0304: Structured Concurrency

**URL:** <https://forums.swift.org/t/se-0304-structured-concurrency/45314>\
**Category:** Proposal Reviews\
**Created:** [March 4, 2021, 4:20pm UTC](https://forums.swift.org/t/se-0304-structured-concurrency/45314 "2021-03-04T16:20:03Z")\
**Posts on this page:** 1\
**Showing post:** 36

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [March 17, 2021, 9:31am UTC](https://forums.swift.org/t/se-0304-structured-concurrency/45314/36 "2021-03-17T09:31:04Z")

</div>

Continuing on API notes then... commenting here on behalf of myself, so others or the core team may disagree – we'll see 🙂

> [@xwu](#):
>
> > [@kiel](#):
> >
> > I feel like an idiot for writing this post because what follows will not substantially address the fantastically complex work done. Concurrency programming isn't my strength and I'm super excited for Swift 6. But I want the user experience of an API to be as elegant as possible, guiding me to do the right thing when I may not be an expert with just the right amount of complexity when I need it.
> 
> I have seen this proposal go through multiple revisions to work through subtle issues related to ergonomics, but I can't shake the overall impression that the end result is more complex than it needs to be. I worry that it's not merely an _aesthetic_ issue but ultimately a _usability_ issue, where we will have solved many sophisticated pain points in very targeted ways, but with an end result that is substantially harder for users coming to the language.

I hear you loud and clear on this one. It's really too bad that `async let` and anything with "nicer to look at syntax" was decided to be punted out of this proposal and into a future one; as really the groups API here is really _not_ what most people will be interacting with at all on a daily basis, unless building your own concurrency "operator" like a scatter-gather or "race" or similar.

So I feel a bit "hands tied" on that – as we said we'll leave the nice syntax and niceties to a future proposal, but let's go through one by one:

* * *

> [@xwu](#):
>
> Near as I can tell, `Task` wears multiple hats:
> 
> - It is a namespace for task-related functions that might otherwise be global functions (and indeed, the `Task.with*Continuation` functions were split out from this proposal and actually made global functions).

Right, yes those did become top level, though mostly due to a lot of prior art with the various `withUnsafe...` functions that already exist and these fitting the same style. Nor are they _really_ about `Task` (much less than all the other ones in this proposal).

> [@xwu](#):
>
> I would strongly suggest jettisoning the use of `Task` as a namespace, and as much as possible to find a principled way to deduplicate static and instance methods that serve the sample purpose.

I feel this one really boils down to personal style and opinions a lot... As a person I do like the `Task` namespace a lot, but I'm just one of the authors and by far _not_ the one with most influence on it 😉 I'll leave that up to the core team to decide as it's a general Swift library style guideline thing.

The reason the namespace is nice is because it's easy to discover similar APIs I might need - e.g. this way I might learn about `Task.local` etc.

* * *

> [@xwu](#):
>
> #### `UnsafeCurrentTask`
> 
> Elsewhere, Swift provides `with*` APIs to expose something that shouldn’t be stored and “used later.” Was such a design considered instead of `unsafeCurrent` ? Is there some consideration which prevents such a design here?

> [@CTMacUser](#):
>
> Could we use an " `withUnsafelyDeterminingTask<R>(do body: (UnsafeCurrentTask?) throws -> R) rethrows -> R?` " function?

I don't think there's any technical reason other than "yet again more nesting".  
Usages of this are likely to be deep in framework and library code so it may not be a "huge" issue.

We had hoped to have `@unmovable` at some point during the initial drafts... which would disallow escaping/moving the value out of the current scope, meaning that the closure would not really be necessary.

I'll leave that to @John_McCall or @Douglas_Gregor to comment if that's a likely future direction or not.

* * *

> [@xwu](#):
>
> it does not make sense to me to try to _pessimize_ the naming of a type.
> 
> To preserve the intention of futures taking a backseat in this design, there are other tools in the API design toolbox; we can look, for example, to the precedent of `Result` deliberately vending a limited API for the purposes that it's meant to serve while making clear that it's not meant to be an alternative to supplant throwing functions.

I'm not sure I agree with this; It is not only "pessimizing" the name, but also setting expectations correctly: Looking at this as a "language immigrant", coming from another language (e.g. Scala, Ruby, JS, Java, Kotlin...), or even just Swift with pervasive PromiseKit, NIO or Combine's Future types seeing a `Future` in Swift would trigger the "I know how to use this, map(), flatMap(), right?" and then disappointment (and maybe finally enlightenment?) 😉

I think it is useful to call this a `Task.Handle` because it will not lead people into cargo culting on "okey, instead of NIO Future we pass now Swift Future" but rather lead into rethinking things a little bit more -- one would hope.

> [@xwu](#):
>
> For the same reason, I think the primitive `Task.runDetached` (as have thought others here on the forums) can have a more succinct name also. Yes, it is a primitive that we're not trying to put forward as the go-to option in concurrency (pun intended). But neither does that mean that we need to namespace and pessimize the naming of it. It would seem to me that a global function merely named `run` , `detach` , `spawn` , `launch` , or anything similar would work. If we're worried that _structured_ concurrency facilities can't compete with that sort of name, then we should _optimize_ the design of the rest of the facilities, not _pessimize_ this one.

Maybe there's a middle ground here?

I–personally–would for example like if we took two verbs and hyper focused on using them throughout the APIs, e.g.: `group.spawn (child task)` and `Task.detach` and if any other forms of child tasks were allowed to be spawned we'd also talk about them using `spawn`. If any other "not child" tasks were allowed to be spawned, we'd use the verb "detach".

I would definitely _not_ want to use `Task.launch` or `Task.new` as the `detach` operation. It looks too inconspicuous but is actually a disaster for priority and tracing propagation. So it has to "shout" that "I am going to abandon all information from my parent task!!!"

* * *

> [@xwu](#):
>
> This isn't the relationship being described here: a `Group` _isn't_ a kind of `Task` , and while it's true that a `Task` can have many `Group` s, the much more salient point we're emphasizing here (the "structure" in structured concurrency) is that a `Group` is something that has many `Task` s.

As I said before, this really feels like an opinion / personal preference thing to be honest, so let's see what the core team comes up with. Yeah it could be either `Task.Group` or `TaskGroup` doesn't _really_ matter.

I will say however that there definitely _will be_ new "concurrency container thingy" types in the future. So whatever we do here affects future APIs like `Task.DetachedLifecycleAttachedToClassContainer` or similar types.

* * *

> [@xwu](#):
>
> > By default, the task group will schedule child tasks added to the group on the default global concurrent executor. The startingChildTasksOn argument can be provided to override this behavior.
> 
> Why does this API need to differ from the preceding API in the text, clarifying that it’s _child tasks_ that are started on an executor, rather than just spelling the argument label just `startingOn` ? Can anything but a task be scheduled on an executor, and could the task group be scheduling anything but its child tasks?

I believe Joe's idea here was to spell out that it is not _the group_ that will run on any other executor, which the following spelling might imply to the reader: `withGroup(startingOn: coolExecutor)` vs. the boringly explicit `withGroup(startingChildTasksOn: coolExecutor)`. We had a number of people be confused if the group itself is a task or not etc. (in fact, it used to be a child task, but isn't anymore).

> [@xwu](#):
>
> > The add operation returns true to indicate that the task was added to the group, and false otherwise. The only circumstance under which the task will not be added to the group is when the task group has been cancelled; see the section on task group cancellation for more information.
> 
> What is the rationale for returning `true` or `false` (an unusual way to signal failure in Swift) versus, say, not returning a value but throwing `CancellationError` ?

The _actual_ proper solution IMHO would be soft-faults here...

I've been hoping for those for a long long time already: [[stdlib] Cleanup callback for fatal Swift errors - #4 by ktoso](https://forums.swift.org/t/stdlib-cleanup-callback-for-fatal-swift-errors/26977/4)

The lack of panics or similar mechanisms in Swift and the very explicit nature of `Errors` forces us into these weird API decisions... Specifically this is about: It is not the operation that might "fail", it is the infrastructure that might. The same appears in networking, or other panics vs errors situations...

But we're not going to get panics / soft faults any time soon I guess. So this design is being forced to think about -- is this automatic cancellation case worth the syntactic noise of having to `try` every single `group.add` even though the vast majority of time those will never throw...?

* * *

> [@xwu](#):
>
> Longstanding convention in U.S. English, and specifically observed in past Apple style guides, is that the word is spelled "canceled" with one "l" (while "cancellation" is always spelled with two).
> 
> I understand there was some attempt to implement this in the APIs here, but it was reverted pending further discussion on review.

Hah yes, there was a PR and was reverted... 🙂 There seems to be both prior art for disregarding the language rules in Apple APIs [e.g. NSOperation](https://developer.apple.com/documentation/foundation/nsoperation/1411672-cancel) (regardless of it being wrong linguistically...). Also some people expressed that they feel super strongly about it, so indeed the current and expected spelling is cancel, cancelled, cancellation - as weird as it it.

---

_[View the full topic](https://forums.swift.org/t/se-0304-structured-concurrency/45314)._
