The advantage is that it provides guarantees, that wouldn't be there otherwise.
Let's for example say one of those 4 tasks would write to a file. With structured concurrency you have a guarantee that once the supertask finishes, the file is no longer in use. Without structured concurrency you won't have that, and a subtask might error on trying to use the file or find garbage data in it. Running the supertask in a loop with structured concurrency would have no risk of running into resource contention, while without it there is that risk.
Plus structured concurrency also helps adding a deterministic bound on the amount of concurrency in the system, while just spawning more background tasks can lead to concurrency grow in an unbounded fashion.
> In Go for example, I don't remember having to clean up fibers manually.
The equivalent in Go would be to wait for subtasks to complete, using the WaitGroup pattern. And to notify the subtasks that they should complete using "Context".
At a bare minimum your code might have different behavior. Your `run! deref` will wait for each future one by one. What if the last one threw an exception while the others are running? You'd probably want the other tasks to get canceled immediately. I'm not sure what Executor.close() does exactly, but in theory it could do this for you automatically. Not something I want to write by hand for sure[1].
> In Go for example, I don't remember having to clean up fibers manually.
And that is awful if you think about it a bit. There's no composition happening, your go block just goes off somewhere without you having any control of it. It's like goto, only it forks first and never gives back the process handle[2].
Hum, handling the first to error and interrupt the others is a good use case.
From what I read, Loom structured concurency will not automatically interrupt the other tasks though. And the .close I think will wait for all of them to complete. They said it's because you might not always want other tasks to cancel, so they made it more explicit.
But arguably, maybe if you had a catch in there it would be triggered on the first task to throw. But I can also imagine a similar function to wait for the first future to return or error in Clojure. Though I'm guessing at that point one might say that's just implementing structured concurency?
My question maybe was about the need to close virtual threads. Are they just abusing Closeable so you can use .close as a wait for all to complete even when errors are thrown feature, or do you actually leak resources if you don't clean up Virtual Threads you're done using?
I don't understand what the advantages of it are?
Are Virtual Threads resources that need to be explicitly cleaned up? Can't the pool grow and shrink based on demand?
In Go for example, I don't remember having to clean up fibers manually.
For example, how is the structured concurency example different from:
Also as an FYI, the example can be simplified by using `with-open`.