<div dir="ltr"><div class="markdown-here-wrapper" style=""><p style="margin:0px 0px 1.2em!important">Eric,</p>
<blockquote style="margin:1.2em 0px;border-left:4px solid rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">Then comes the question of Scope…<br>I proposed something like this a long time ago, but at the time people claimed this was unnecessary abstraction</p>
</blockquote>
<p style="margin:0px 0px 1.2em!important">I am not sure when exactly you talked about this so I don’t have a full timeline, but I talked in the mailing list of Loom and of Amber several time about the problem in Java’s current limitations of “try-with-resources”, and I was told that there is a discussion about improving it. Having a stronger TWR mechanism that has out-of-the-box composition, and forces safe-use is a direct generalisation of an abstract Scope object.<br>I actually thought about bringing this up again in a separate conversation when I will have a bit of free time, I think this is an extremely important tool to add to the Java ecosystem.</p>
<blockquote style="margin:1.2em 0px;border-left:4px solid rgb(221,221,221);padding:0px 1em;color:rgb(119,119,119);quotes:none">
<p style="margin:0px 0px 1.2em!important">but I want to say something (not well thought out yet) like</p>
</blockquote>
<p style="margin:0px 0px 1.2em!important">Coincidentally, I have started working on something similar to this, but stopped because of (1) time problems and (2) fighting Java’s compiler’s plugin API wasn’t fun.<br>My idea was that if you start a structured-task, the plugin that is shipped with the library will verify on compile time that you don’t use any scoped-variables that are not initialized. I am planning on finishing this when my current occupation will end (which will be in about 10 months unfortunately)</p>
<p style="margin:0px 0px 1.2em!important">Attila (and still Eric)<br>It seems like both of you don’t like the TWR part, but I find it extremely nice to have, and a lot of times prefer it over a lambda argument.<br>TWR allows us to propagate checked exceptions, which till we add union-types in generic levels is impossible to do with lambdas, and it doesn’t have the (justified) restrictions of lambdas.<br>Scoped-concurrency are meant to be used with “standard” Java coding-style, so the restrictions on lambdas is a big deal.<br>I think that:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;margin:1.2em 0px"><code class="hljs language-java" style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;overflow:auto;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block!important;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,248)">var scopeBuilder = StructuredTask.ScopeBuilder()
.value<String>(<span class="hljs-string" style="color:rgb(221,17,68)">"USERNAME"</span>, <span class="hljs-string" style="color:rgb(221,17,68)">"duke"</span>);
<span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">try</span> (var scope = scopeBuilder.build(strategy)) {
...
}
<span class="hljs-comment" style="color:rgb(153,153,136);font-style:italic">// alternatively</span>
var scopeBuilder = StructuredTask.ScopeBuilder(strategy)
.value<String>(<span class="hljs-string" style="color:rgb(221,17,68)">"USERNAME"</span>, <span class="hljs-string" style="color:rgb(221,17,68)">"duke"</span>);
<span class="hljs-keyword" style="color:rgb(51,51,51);font-weight:bold">try</span> (var scope = scopeBuilder.start()) {
...
}
</code></pre>
<p style="margin:0px 0px 1.2em!important">Is better (note that this is currently impossible because of how scopedValued are implemented. Something I talked about in this mailing-list long ago and was pushed back because Java’s TWR is not good enough to guarantee no memory leaks from mis-using ScopedValues), and to allow a different writing-style, we can add a new “StructuredTask” implementation, of “Stream”:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;margin:1.2em 0px"><code class="hljs language-java" style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;overflow:auto;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block!important;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,248)">var results = StructuredTask.StreamBuilder()
.value<String>(<span class="hljs-string" style="color:rgb(221,17,68)">"USERNAME"</span>, <span class="hljs-string" style="color:rgb(221,17,68)">"duke"</span>)
.flatFork(() -> Stream.of(workers))
.join()
.throwIfFailed() <span class="hljs-comment" style="color:rgb(153,153,136);font-style:italic">// "StructuredTask.Stream" doesn't need a "strategy" because it exposes functions like this instead</span>
.map(Subtask::get) <span class="hljs-comment" style="color:rgb(153,153,136);font-style:italic">// "structured" comes into play here, you can't call terminating operators without first calling "join"</span>
.toList();
</code></pre>
<div title="MDH:RXJpYyw8YnI+Jmd0OyBUaGVuIGNvbWVzIHRoZSBxdWVzdGlvbiBvZiBTY29wZS4uLjxwPiZndDsg
SSBwcm9wb3NlZCBzb21ldGhpbmcgbGlrZSB0aGlzIGEgbG9uZyB0aW1lIGFnbywgYnV0IGF0IHRo
ZSB0aW1lIHBlb3BsZSBjbGFpbWVkIHRoaXMgd2FzIHVubmVjZXNzYXJ5IGFic3RyYWN0aW9uPC9w
PjxwPjxicj48L3A+PHA+SSBhbSBub3Qgc3VyZSB3aGVuIGV4YWN0bHkgeW91IHRhbGtlZCBhYm91
dCB0aGlzIHNvIEkgZG9uJ3QgaGF2ZSBhIGZ1bGwgdGltZWxpbmUsIGJ1dCBJIHRhbGtlZCBpbiB0
aGUgbWFpbGluZyBsaXN0IG9mIExvb20gYW5kIG9mIEFtYmVyIHNldmVyYWwgdGltZSBhYm91dCB0
aGUgcHJvYmxlbSBpbiBKYXZhJ3MgY3VycmVudCBsaW1pdGF0aW9ucyBvZiAidHJ5LXdpdGgtcmVz
b3VyY2VzIiwgYW5kIEkgd2FzIHRvbGQgdGhhdCA8aT50aGVyZSBpczwvaT4mbmJzcDthIGRpc2N1
c3Npb24gYWJvdXQgaW1wcm92aW5nIGl0LiBIYXZpbmcgYSBzdHJvbmdlciBUV1IgbWVjaGFuaXNt
IHRoYXQgaGFzIG91dC1vZi10aGUtYm94IGNvbXBvc2l0aW9uLCBhbmQgZm9yY2VzIHNhZmUtdXNl
IGlzIGEgZGlyZWN0IGdlbmVyYWxpc2F0aW9uIG9mIGFuIGFic3RyYWN0IFNjb3BlIG9iamVjdC48
L3A+PHA+SSBhY3R1YWxseSB0aG91Z2h0IGFib3V0IGJyaW5naW5nIHRoaXMgdXAgYWdhaW4gaW4g
YSBzZXBhcmF0ZSBjb252ZXJzYXRpb24gd2hlbiBJIHdpbGwgaGF2ZSBhIGJpdCBvZiBmcmVlIHRp
bWUsIEkgdGhpbmsgdGhpcyBpcyBhbiBleHRyZW1lbHkgaW1wb3J0YW50IHRvb2wgdG8gYWRkIHRv
IHRoZSBKYXZhIGVjb3N5c3RlbS48L3A+PHA+Jmd0OyBidXQgSSB3YW50IHRvIHNheSBzb21ldGhp
bmcgKG5vdCB3ZWxsIHRob3VnaHQgb3V0IHlldCkgbGlrZTwvcD48cD48YnI+PC9wPjxjb2RlPjwv
Y29kZT48cD5Db2luY2lkZW50YWxseSwgSSBoYXZlIHN0YXJ0ZWQgd29ya2luZyBvbiBzb21ldGhp
bmcgc2ltaWxhciB0byB0aGlzLCBidXQgc3RvcHBlZCBiZWNhdXNlIG9mICgxKSB0aW1lIHByb2Js
ZW1zIGFuZCAoMikgZmlnaHRpbmcgSmF2YSdzIGNvbXBpbGVyJ3MgcGx1Z2luIEFQSSB3YXNuJ3Qg
ZnVuLiZuYnNwOzwvcD48cD5NeSBpZGVhIHdhcyB0aGF0IGlmIHlvdSBzdGFydCBhIHN0cnVjdHVy
ZWQtdGFzaywgdGhlIHBsdWdpbiB0aGF0IGlzIHNoaXBwZWQgd2l0aCB0aGUgbGlicmFyeSB3aWxs
IHZlcmlmeSBvbiBjb21waWxlIHRpbWUgdGhhdCB5b3UgZG9uJ3QgdXNlIGFueSBzY29wZWQtdmFy
aWFibGVzIHRoYXQgYXJlIG5vdCBpbml0aWFsaXplZC4gSSBhbSBwbGFubmluZyBvbiBmaW5pc2hp
bmcgdGhpcyB3aGVuIG15IGN1cnJlbnQgb2NjdXBhdGlvbiB3aWxsIGVuZCAod2hpY2ggd2lsbCBi
ZSBpbiBhYm91dCAxMCBtb250aHMgdW5mb3J0dW5hdGVseSk8L3A+PHA+PGJyPjwvcD48cD5BdHRp
bGEgKGFuZCBzdGlsbCBFcmljKTwvcD48cD5JdCBzZWVtcyBsaWtlIGJvdGggb2YgeW91IGRvbid0
IGxpa2UgdGhlIFRXUiBwYXJ0LCBidXQgSSBmaW5kIGl0IGV4dHJlbWVseSBuaWNlIHRvIGhhdmUs
IGFuZCBhIGxvdCBvZiB0aW1lcyBwcmVmZXIgaXQgb3ZlciBhIGxhbWJkYSBhcmd1bWVudC48L3A+
PHA+VFdSIGFsbG93cyB1cyB0byBwcm9wYWdhdGUgY2hlY2tlZCBleGNlcHRpb25zLCB3aGljaCB0
aWxsIHdlIGFkZCB1bmlvbi10eXBlcyBpbiBnZW5lcmljIGxldmVscyBpcyBpbXBvc3NpYmxlIHRv
IGRvIHdpdGggbGFtYmRhcywgYW5kIGl0IGRvZXNuJ3QgaGF2ZSB0aGUgKGp1c3RpZmllZCkgcmVz
dHJpY3Rpb25zIG9mIGxhbWJkYXMuPC9wPjxwPlNjb3BlZC1jb25jdXJyZW5jeSBhcmUgbWVhbnQg
dG8gYmUgdXNlZCB3aXRoICJzdGFuZGFyZCIgSmF2YSBjb2Rpbmctc3R5bGUsIHNvIHRoZSByZXN0
cmljdGlvbnMgb24gbGFtYmRhcyBpcyBhIGJpZyBkZWFsLjwvcD48cD5JIHRoaW5rIHRoYXQ6PC9w
PjxwPmBgYGphdmE8L3A+PHA+PC9wPjxkaXY+dmFyIHNjb3BlQnVpbGRlciA9IFN0cnVjdHVyZWRU
YXNrLlNjb3BlQnVpbGRlcigpPC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsg
Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAm
bmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOy52YWx1ZSZsdDtTdHJpbmcm
Z3Q7KCJVU0VSTkFNRSIsICJkdWtlIik7PGJyPjwvZGl2PjxkaXY+dHJ5ICh2YXIgc2NvcGUgPSBz
Y29wZUJ1aWxkZXIuYnVpbGQoc3RyYXRlZ3kpKSB7PC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7IC4u
LjwvZGl2PjxkaXY+fTwvZGl2PjxkaXY+Ly8gYWx0ZXJuYXRpdmVseTwvZGl2PjxkaXY+PGRpdj52
YXIgc2NvcGVCdWlsZGVyID0gU3RydWN0dXJlZFRhc2suU2NvcGVCdWlsZGVyKHN0cmF0ZWd5KTwv
ZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7
ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsg
Jm5ic3A7ICZuYnNwOyAmbmJzcDsudmFsdWUmbHQ7U3RyaW5nJmd0OygiVVNFUk5BTUUiLCAiZHVr
ZSIpOzxicj48L2Rpdj48ZGl2PnRyeSAodmFyIHNjb3BlID0gc2NvcGVCdWlsZGVyLnN0YXJ0KCkp
IHs8L2Rpdj48ZGl2PiZuYnNwOyAmbmJzcDsgLi4uPC9kaXY+PGRpdj59PC9kaXY+PC9kaXY+PGRp
dj5gYGA8L2Rpdj48ZGl2PklzIGJldHRlciAobm90ZSB0aGF0IHRoaXMgaXMgY3VycmVudGx5IGlt
cG9zc2libGUgYmVjYXVzZSBvZiBob3cgc2NvcGVkVmFsdWVkIGFyZSBpbXBsZW1lbnRlZC4gU29t
ZXRoaW5nIEkgdGFsa2VkIGFib3V0IGluIHRoaXMgbWFpbGluZy1saXN0IGxvbmcgYWdvIGFuZCB3
YXMgcHVzaGVkIGJhY2sgYmVjYXVzZSBKYXZhJ3MgVFdSIGlzIG5vdCBnb29kIGVub3VnaCB0byBn
dWFyYW50ZWUgbm8gbWVtb3J5IGxlYWtzIGZyb20gbWlzLXVzaW5nIFNjb3BlZFZhbHVlcyksIGFu
ZCB0byBhbGxvdyBhIGRpZmZlcmVudCB3cml0aW5nLXN0eWxlLCB3ZSBjYW4gYWRkIGEgbmV3ICJT
dHJ1Y3R1cmVkVGFzayIgaW1wbGVtZW50YXRpb24sIG9mICJTdHJlYW0iOjwvZGl2PjxkaXY+PGJy
PjwvZGl2PjxkaXY+YGBgamF2YTwvZGl2PjxkaXY+dmFyIHJlc3VsdHMgPSBTdHJ1Y3R1cmVkVGFz
ay5TdHJlYW1CdWlsZGVyKCk8L2Rpdj48ZGl2PiZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAm
bmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZu
YnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7LnZhbHVlJmx0O1N0cmluZyZn
dDsoIlVTRVJOQU1FIiwgImR1a2UiKTwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5i
c3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJz
cDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsuZmxhdEZvcmsoKCkg
LSZndDsgU3RyZWFtLm9mKHdvcmtlcnMpKTwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAmbmJzcDsg
Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAm
bmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsuam9pbigpPGJy
PjwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5i
c3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJz
cDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsudGhyb3dJZkZhaWxlZCgpIC8vICJTdHJ1Y3R1cmVkVGFz
ay5TdHJlYW0iIGRvZXNuJ3QgbmVlZCBhICJzdHJhdGVneSIgYmVjYXVzZSBpdCBleHBvc2VzIGZ1
bmN0aW9ucyBsaWtlIHRoaXMgaW5zdGVhZDwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAmbmJzcDsg
Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAm
bmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsubWFwKFN1YnRh
c2s6OmdldCkgLy8gInN0cnVjdHVyZWQiIGNvbWVzIGludG8gcGxheSBoZXJlLCB5b3UgY2FuJ3Qg
Y2FsbCB0ZXJtaW5hdGluZyBvcGVyYXRvcnMgd2l0aG91dCBmaXJzdCBjYWxsaW5nICJqb2luIjxi
cj48L2Rpdj48ZGl2PiZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZu
YnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5i
c3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7LnRvTGlzdCgpOzwvZGl2PjxkaXY+YGBgPC9kaXY+PGRp
dj48YnI+PC9kaXY+" style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0"></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, May 19, 2023 at 9:33 PM Attila Kelemen <<a href="mailto:attila.kelemen85@gmail.com">attila.kelemen85@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">There could be some restrictions which I didn't notice, but STS takes<br>
a `ThreadFactory`, so it is not necessarily related to virtual threads<br>
(though I guess people will use it with virtual threads pretty much<br>
exclusively for performance reasons).<br>
<br>
> ... but it's not clear to me that StructuredTaskScope implies ScopedValue?<br>
<br>
Scoped values are captured by STS upon its creation and are propagated<br>
to forked tasks. So, the "Scope" in those names are well justified<br>
(and the normal calls naturally inherit scoped values of course like a<br>
thread local).<br>
<br>
As for combining scoped values and STS into a single builder: It<br>
doesn't seem that useful to me, because you don't just want to share<br>
the scoped values with an STS, but normal calls as well usually (at<br>
least the use cases I can think of, you would not immediately start an<br>
STS, but just call a method). However, the part where you don't have<br>
to use try-with-resources is good of course, but it is an easy to<br>
write utility (having the convenience in the JDK would be nice though<br>
of course).<br>
<br>
Eric Kolotyluk <<a href="mailto:eric@kolotyluk.net" target="_blank">eric@kolotyluk.net</a>> ezt írta (időpont: 2023. máj. 19., P, 1:07):<br>
><br>
> Just some thoughts on Threads, Task, etc... please ignore if you think I am out to lunch...<br>
><br>
> Originally, Project Loom started with the notion of Fibers, but then change the name to VirtualThreads. I liked "Fibers" but I agree that "VirtualTreads" makes more sense in some ways.<br>
><br>
> When we talk about "Tasks" we usually think about concurrent things, where a Task is an abstract concept of concurrency and a Thread is an implementation.<br>
><br>
> Task is what<br>
><br>
> Thread is how<br>
><br>
> If we changed the term "VirtualThread" to "Subthread" then there would be better symmetry with "Task" and "Subtask" but it's far too late to change the name "VirtualThread" and I am not sure if I like the term "VirtualTask" but I could live with it.<br>
><br>
> In a way, we could create a symmetry between Task and Thread, where every Task has a Thread. Maybe this is a useless symmetry, but creating a Task does not mean you have to start the thread, just that it's the root of abstraction on concurrency, where thread is the root of implementation.<br>
><br>
> Then comes the question of Scope...<br>
><br>
> I proposed something like this a long time ago, but at the time people claimed this was unnecessary abstraction, and then later came Structured Concurrency... oh well...<br>
><br>
> I have since seen discussions on StructuredTaskScope and now ScopedValue, but it's not clear to me that StructuredTaskScope implies ScopedValue? It doesn't but it can. Both StructuredTaskScope and ScopedValue are examples of dynamic scope.<br>
><br>
> Maybe instead of saying<br>
><br>
> try (var scope = new StructuredTaskScope<Object>()) {<br>
><br>
> Subtask<String> subtask1 = scope.fork(task1);<br>
> Subtask<Integer> subtask2 = scope.fork(task2);<br>
><br>
> scope.join();<br>
><br>
> ... process results/exceptions ...<br>
><br>
> } // close<br>
><br>
> we should be saying<br>
><br>
> try (var scope = Task.StructuredScope<Object>()) {<br>
><br>
> Subtask<String> subtask1 = scope.fork(task1);<br>
> Subtask<Integer> subtask2 = scope.fork(task2);<br>
><br>
> scope.join();<br>
><br>
> ... process results/exceptions ...<br>
><br>
> } // close<br>
><br>
> where we have to figure out what we really mean by "Task" and "Subtask"<br>
><br>
> Given that Subtask is a sub-interface of Supplier<T> , maybe Task should also be a sub-interface of Supplier<C> where C is some Collection class. This might make concurrent collection based coding more elegant, where we state our intention to 'Supply' some collection via get().<br>
><br>
> Where we have<br>
><br>
> private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();<br>
><br>
> ScopedValue.runWhere(USERNAME, "duke", () -> {<br>
> try (var scope = new StructuredTaskScope<String>()) {<br>
><br>
> scope.fork(() -> childTask1());<br>
> scope.fork(() -> childTask2());<br>
> scope.fork(() -> childTask3());<br>
><br>
> ...<br>
> }<br>
> });<br>
><br>
> we might have<br>
><br>
> private static final Scope.Value<String> USERNAME = Scope.Value();<br>
><br>
> Scope.Value.runWhere(USERNAME, "duke", () -> {<br>
> try (var scope = new Task.StructuredScope<String>()) {<br>
><br>
> scope.fork(() -> childTask1());<br>
> scope.fork(() -> childTask2());<br>
> scope.fork(() -> childTask3());<br>
><br>
> ...<br>
> }<br>
> });<br>
><br>
> but I want to say something (not well thought out yet) like<br>
><br>
> List<String> results = Task.Builder(workers)<br>
> .scope.value<String>("USERNAME", "duke")<br>
> .scope.structured<String>( context-> {<br>
> List<Subtask<T>> subtasks<br>
> = workers.stream().map(scope::fork).toList();<br>
> context.join()<br>
> .throwIfFailed(); // Propagate exception if any subtask fails<br>
> // Here, all tasks have succeeded, so compose their results<br>
> return subtasks.stream().map(Subtask::get).toList();<br>
> })<br>
> .start();<br>
><br>
> Where the "try" is implicitly there... where it's boilerplate I don't need to see. I am learning to appreciate the Builder Pattern a little more, and wondering if Loom could make better use of it.<br>
><br>
> Anyway, just thinking out loud, and perhaps these discussions are better shared over beer? I know that the Scala community prefer Scotch, primarily Ardbeg, which is perhaps why Scala Architecture looks different than Java Architecture ;-)<br>
><br>
> Again<br>
><br>
> There are only two hard things in Computer Science: cache invalidation and naming things.<br>
><br>
> -- Phil Karlton<br>
><br>
><br>
</blockquote></div><br clear="all"><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div dir="ltr">Holo The Wise Wolf Of Yoitsu</div></div>