<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"David" <david.vlijmincx@gmail.com><br><b>To: </b>"loom-dev" <loom-dev@openjdk.org><br><b>Sent: </b>Monday, September 11, 2023 11:07:59 PM<br><b>Subject: </b>Business policy inside a StructuredTaskScope<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><div dir="ltr">Hi,<br><br>I have a question about where the business logic / shutdown policy is supposed to be when you extend from a StructuredTaskScope. Looking at online examples it seems like the business logic is supposed to be in the handleComplete method of a class that extends StructuredTaskScope, but is this not too far away from the place it is being used?<br><div>Currently, I am using the following scope as it keeps the shutdown policy closer to where the scope is used in my code, but I do not know if this is the correct way of creating your own scopes. </div><br><div><div><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace;font-size:9.8pt"><span style="color:rgb(0,51,179)">class </span><span style="color:rgb(0,0,0)">TriggerScope</span><<span style="color:rgb(0,126,138)">T</span>> <span style="color:rgb(0,51,179)">extends </span><span style="color:rgb(0,0,0)">StructuredTaskScope</span><<span style="color:rgb(0,126,138)">T</span>> {<br><br> <span style="color:rgb(0,51,179)">final private </span><span style="color:rgb(0,0,0)">Function</span><<span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><? <span style="color:rgb(0,51,179)">extends </span><span style="color:rgb(0,126,138)">T</span>>, <span style="color:rgb(0,0,0)">Boolean</span>> <span style="color:rgb(135,16,148)">trigger</span>;<br><br> <span style="color:rgb(0,98,122)">TriggerScope</span>(<span style="color:rgb(0,0,0)">Function</span><<span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><? <span style="color:rgb(0,51,179)">extends </span><span style="color:rgb(0,126,138)">T</span>>, <span style="color:rgb(0,0,0)">Boolean</span>> trigger) {<br> <span style="color:rgb(0,51,179)">this</span>.<span style="color:rgb(135,16,148)">trigger </span>= trigger;<br> }<br><br> <span style="color:rgb(158,136,13)">@Override<br></span><span style="color:rgb(0,51,179)">protected void </span><span style="color:rgb(0,98,122)">handleComplete</span>(<span style="color:rgb(0,0,0)">Subtask</span><? <span style="color:rgb(0,51,179)">extends </span><span style="color:rgb(0,126,138)">T</span>> subtask) {<br> <span style="color:rgb(0,51,179)">if </span>(<span style="color:rgb(135,16,148)">trigger</span>.apply(subtask)) {<br> shutdown();<br> }<br> }<br>}</pre>The above scope allows me to implement a new scope like below:<pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace;font-size:9.8pt"><span style="color:rgb(0,51,179)">public static void </span><span style="color:rgb(0,98,122)">main</span>(<span style="color:rgb(0,0,0)">String</span>[] args) <span style="color:rgb(0,51,179)">throws </span><span style="color:rgb(0,0,0)">InterruptedException </span>{<br><br> <span style="color:rgb(0,0,0)">Main main </span>= <span style="color:rgb(0,51,179)">new </span>Main();<br><br> <span style="color:rgb(0,51,179)">try </span>(<span style="color:rgb(0,51,179)">var </span><span style="color:rgb(0,0,0)">scope </span>= <span style="color:rgb(0,51,179)">new </span>TriggerScope<<span style="color:rgb(0,0,0)">Product</span>>(<span style="color:rgb(0,0,0)">main</span>::trigger)) {<br><br> <span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><<span style="color:rgb(0,0,0)">Product</span>> <span style="color:rgb(0,0,0)">fork </span>= <span style="color:rgb(0,0,0)">scope</span>.fork(() -> <span style="color:rgb(0,51,179)">new </span>Product(<span style="color:rgb(23,80,235)">50</span>));<br> <span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><<span style="color:rgb(0,0,0)">Product</span>> <span style="color:rgb(0,0,0)">fork1 </span>= <span style="color:rgb(0,0,0)">scope</span>.fork(() -> <span style="color:rgb(0,51,179)">new </span>Product(<span style="color:rgb(23,80,235)">100</span>));<br> <span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><<span style="color:rgb(0,0,0)">Product</span>> <span style="color:rgb(0,0,0)">fork2 </span>= <span style="color:rgb(0,0,0)">scope</span>.fork(() -> <span style="color:rgb(0,51,179)">new </span>Product(<span style="color:rgb(23,80,235)">150</span>));<br><br> <span style="color:rgb(0,0,0)">scope</span>.join();<br> }<br><br>}<br><br><span style="color:rgb(0,51,179)">private boolean </span><span style="color:rgb(0,98,122)">trigger</span>(<span style="color:rgb(0,0,0)">StructuredTaskScope</span>.<span style="color:rgb(0,0,0)">Subtask</span><? <span style="color:rgb(0,51,179)">extends </span><span style="color:rgb(0,0,0)">Product</span>> subtask) {<br> <span style="color:rgb(0,51,179)">if </span>(<span style="color:rgb(0,0,0)">Subtask</span>.<span style="color:rgb(0,0,0)">State</span>.<span style="color:rgb(135,16,148);font-style:italic">SUCCESS</span>.equals(subtask.state()) && subtask.get().price() > <span style="color:rgb(23,80,235)">50 </span>&& subtask.get().price() < <span style="color:rgb(23,80,235)">150</span>){<br> <span style="color:rgb(0,0,0)">System</span>.<span style="color:rgb(135,16,148);font-style:italic">out</span>.println(<span style="color:rgb(6,125,23)">"result: " </span>+ subtask.get().price());<br> <span style="color:rgb(0,51,179)">return true</span>;<br> }<br> <span style="color:rgb(0,51,179)">return false</span>;<br>}</pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace;font-size:9.8pt"><br></pre><font face="arial, sans-serif">In my opinion, this way is more convenient, but is this still a correct implementation or does this deviate too much from the original idea of creating a scope in its own class?<br><br>Looking forward to hearing your thoughts.</font></pre></div></div></div></blockquote><div><br></div><div>It is Ok but a little dangerous if you start to add fields to the Main object to retrive the price instead of printing it, perhaps it's better to declare tigger static and use Main::trigger instead of main::trigger.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div><div>In term of code, State.Sucess is an enum value so you can use == to compare it with the state, instead of calling subtasl.get().price() several times you can store it in a local variable and trigger should be typed as a Predicate (a function that return a boolean).</div></div><div><br data-mce-bogus="1"></div><div>Now, i agree with you that storing the business logic iside the STS is a king of ugly.<br data-mce-bogus="1"></div><div>That's why I want to add a STS that see the tasks as a stream, to avoid to store the business logic inside a STS, here is your code rewritten using a Streamable STS.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div> static boolean isValid(Subtask<Product> task) {</div><div><div> if (task.state() != SUCCESS) {</div><div> return false;</div><div> }</div> var price = task.get().price();<br data-mce-bogus="1"></div><div> return price > 50 && price < 150;<br data-mce-bogus="1"></div><div> }<br data-mce-bogus="1"></div><div> ...<br data-mce-bogus="1"></div><div> Optional<Product> resultingProduct;<br data-mce-bogus="1"></div><div> try(var scope = new StructuredTaskScope.Streamable<Product>>()) { // need a better name<br data-mce-bogus="1"></div><div> scope.fork(() -> new Product(50));<br data-mce-bogus="1"></div><div> scope.fork(() -> new Product(100));<br data-mce-bogus="1"></div><div> scope.fork(() -> new Product(150));<br data-mce-bogus="1"></div><div> resultingProduct = scope.joinWhile(stream -> stream.filter(Utils::isValid).map(Subtask::get).findFirst());<br data-mce-bogus="1"></div><div> }<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div>This code can also be written with one mapMulti() instead of filter() + map(), but given that not a lot of people known how mapMulti() works, i've preferred to write this version.<br data-mce-bogus="1"><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><div dir="ltr"><div><div><pre><font face="arial, sans-serif"><br>Kind regards,<br>David</font></pre></div></div></div></blockquote><div><br></div><div>regards,<br data-mce-bogus="1"></div><div>RĂ©mi<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div></div></div></body></html>