Hey, I've been trying out your idea, but I can't find a single place where doing it would apply.
I understand the idea of nesting scopes well enough. For example, I could put a innerScope inside of a outerscope.fork() call. Alternatively, I could have another scope after the outerScope.join() call. But doing it the first way would mean that I am creating an entire scope for each outerScope subtask. Is that what you are proposing? That makes sense if there are multiple tasks worth of processing that I want. For example, if the outerscope subtask would return a list of things, each of which, I could do further processing on. But that doesn't make sense if each outerScope subtask has only one "thing" that needs further downstream processing. The entire point of a scope is to do multi-threading. Having one task defeats the purpose of that. And doing it the second way means I would have to wait for all of my tasks to complete from the outerScope before the innerScope could do anything. In both cases, I wouldn't really be able to gain anything for my composed joiners. And to be clear, most of my Joiner compositions were simply nesting and adding to an existing implementation. For example, I had a joiner that closed the scope after 3 counts of tasks failing with SomeException. Well, I then used composition to make it cancel AND return the tasks up to and including the 3 failed. In that case, I don't see how I would gain anything by adding another scope. Could you help me see what you had in mind? Maybe I am just lacking creativity. On Fri, Aug 15, 2025 at 8:04 PM David Alayachew <[email protected]> wrote: > Oh, I'm already doing that. Lol, I love STS BECAUSE nesting scopes is so > easy to do. > > But that's an interesting idea. I kind of see what you mean -- maybe I am > stuffing too much logic into a single Joiner. I can't tell yet, so I guess > I'll just have to try it out and get back to you. > > Thanks for the tip. > > > On Fri, Aug 15, 2025, 5:09 PM Viktor Klang <[email protected]> > wrote: > >> Hi David, >> >> Thanks for the added detail, that really helps my understanding of your >> situation. >> >> Did you try/consider/evaluate nested scopes (each with different Joiner >> strategies) over composing Joiners themselves? >> And if you did, what were your findings when comparing those two >> different approaches? >> >> Cheers, >> √ >> >> >> *Viktor Klang* >> Software Architect, Java Platform Group >> Oracle >> ------------------------------ >> *From:* David Alayachew <[email protected]> >> *Sent:* Friday, 15 August 2025 20:53 >> *To:* Viktor Klang <[email protected]> >> *Cc:* loom-dev <[email protected]> >> *Subject:* [External] : Re: My experience with Structured Concurrency >> >> One other detail I'd like to highlight. >> >> Much like Collectors and Gatherers, there are a handful of super useful >> ones that you use everywhere, and then the rest are ad-hoc, inline ones >> where you sort of just make your own to handle a custom scenario. If you >> use streams often, you will run into those frequently, and that's why those >> factory methods are fantastic. >> >> Well, I have kind of found myself in the same position for Joiners. >> Joiners aren't as complex as Collectors and Gatherers, so there has >> certainly been less need for it. But I am also only a few weeks into using >> Joiners (though, I used STS for over a year). If I feel this strain now, >> then I feel like this experience is definitely worth sharing. >> >> On Fri, Aug 15, 2025, 2:44 PM David Alayachew <[email protected]> >> wrote: >> >> Sure. >> >> Long story short, the biggest reason why STS is so useful for me is >> because it allows me to fire off a bunch of requests, and handle their >> failures and outcomes centrally. That is the single most useful feature of >> this library for me. It's also why Future.status was not so useful for me >> -- it calls get under the hood, and therefore might fail! Handling that was >> too much scaffolding. >> >> So, when someone recently challenged me to use Joiners (rather than the >> old STS preview versions I was used to), I started creating Joiners to >> handle all sorts of failure and outcomes. At first, a lot of them could be >> handled by the Joiner.awaitUntil(), where I would just check and see if the >> task failed, then handle the error. But as I got further and further along, >> I started needing to add state to my Joiners in order to get the failure >> handling that I wanted. For example, if a certain number of timeouts occur, >> cancel the scope. Well, that necessitates an AtomicNumber. >> >> Then, as the error-handling got more and more complex, I started finding >> myself making a whole bunch of copy paste, minor variations of similar >> Joiners. Which isn't bad or wrong, but started to feel some strain. Now, I >> need to jump through an inheritance chain just to see what my Joiner is >> really doing. It wasn't so bad, but I did start to feel a little uneasy. >> Bad memories. >> >> So, the solution to a problem like this is to create a Joiner factory. >> Which is essentially what I started to write before I started remembering >> how Collectors and Gatherers worked. At that point, I kind of realized that >> this is worth suggesting, which prompted me to write my original email. >> >> Like I said, not a big deal if you don't give it to me -- I can just make >> my own. >> >> But yes, that is the surrounding context behind that quote. Let me know >> if you need more details. >> >> >> On Fri, Aug 15, 2025, 9:25 AM Viktor Klang <[email protected]> >> wrote: >> >> Hi David, >> >> First of all—thank you for your feedback! >> >> I'm curious to learn more about why you ended up in the situation you >> describe below, specifically about what use-cases led you into wishing for >> an augmentation to Joiner to facilitate composition. >> >> Are you able to share more details? >> >> >Which, funnily enough, led to a slightly different problem -- I found >> myself wanting an easier way to create Joiners. Since I was leaning on >> Joiners so much more heavily than I was for STS, I ended up creating many >> Joiners that do almost the same thing, with just minor variations. And >> inheritance wasn't always the right answer, as I can't inherit from >> multiple classes. Plus, most of my joiners were stateful, but I only wanted >> the non-stateful parts of it. I could do composition, but it sort of felt >> weird to delegate to multiple other Joiners. >> >> Cheers, >> √ >> >> >> *Viktor Klang* >> Software Architect, Java Platform Group >> Oracle >> ------------------------------ >> *From:* loom-dev <[email protected]> on behalf of David >> Alayachew <[email protected]> >> *Sent:* Friday, 15 August 2025 11:52 >> *To:* loom-dev <[email protected]> >> *Subject:* My experience with Structured Concurrency >> >> Hello @loom-dev <[email protected]>, >> >> I just wanted to share my experience with Structured Concurrency. I had >> actually been using it for a while now, but only recently got experience >> with the new Joiner. After trying it out, my previously stated opinion has >> changed. >> >> Overall, Structured Concurrency has been a pleasure. I'll avoid repeating >> ALL my old thoughts and just highlight the KEY details. >> >> * Structured Concurrency is excellent for complex error-handling. >> Receiving exceptions via the subtask makes all the error-handling less >> painful. >> * Structured Concurrency makes nesting scopes a breeze, a task I >> historically found very painful to do. >> * Inheritance allows me to take an existing Scope (now Joiner), and >> modify only what I need to in order to modify it for my use case. Great for >> reusing old strategies in new ways. >> >> Now for the new stuff -- having Joiner be the point of extension >> definitely proved to be the right move imo. I didn't mention this in my >> original message, but while it was easy to get a scope set up using >> inheritance, it wasn't always clear what invariants needed to be >> maintained. For example, the ensureOwnerAndJoined method. Was that >> something we needed to call when inheriting? On which methods? Just join()? >> >> The Joiner solution is comparatively simpler, which actually meant that I >> ended up creating way more Joiners, rather than only several STS'. Joiners >> invariants are obvious, and there is no ambiguity on what is expected from >> the implementor. >> >> Which, funnily enough, led to a slightly different problem -- I found >> myself wanting an easier way to create Joiners. Since I was leaning on >> Joiners so much more heavily than I was for STS, I ended up creating many >> Joiners that do almost the same thing, with just minor variations. And >> inheritance wasn't always the right answer, as I can't inherit from >> multiple classes. Plus, most of my joiners were stateful, but I only wanted >> the non-stateful parts of it. I could do composition, but it sort of felt >> weird to delegate to multiple other Joiners. >> >> Part of me kept wondering how well a factory method, similar to the ones >> for Collectors and Gatherers, might fare for Joiners. >> >> Regardless, even if we don't get that factory method, this library has >> been a pleasure, and I can't wait to properly implement this once it goes >> live. >> >> Thank you for your time and consideration. >> David Alayachew >> >>
