----- Original Message -----
> From: "Jochen Theodorou" <[email protected]>
> To: "dev" <[email protected]>
> Sent: Thursday, April 2, 2026 6:15:03 PM
> Subject: Re: Feedback requested for Groovy 6

Hello Jochen,

> On 4/2/26 11:08, Remi Forax wrote:
> [...]
>>> https://groovy.apache.org/blog/groovy-async-await
>>>
>>> Inspired by similar constructs in JavaScript, C#, Kotlin, and Swift,
>>> the proposal lets you write asynchronous code in a sequential,
>>> readable style. This allows developers to leverage virtual threads on
>>> our compute environments, which have ever increasing processing power,
>>> but without the complexity of writing ad-hoc multi-threaded code.
>>> (This is quite mature and ready for merging but hasn't seen widespread
>>> testing outside a few folks in the development team)
>> 
>> Hello Paul,
>> I think you reach peak complexity (as Brian Goetz would say) with this 
>> feature.
>> 
>> There are 3 problems with CompletableFeature,
>> the code is not very readable and forgetting about exceptional case is too 
>> easy,
>> you have no stacktrace so when an error occurs, you are lost, you can not
>> profile it (because there is no stacktrace) so if your code is slow, you are
>> lost.
>> 
>> Groovy is not only about the syntax, in my opinion, it's also about the 
>> runtime
>> experience, the fast feedback, you hit a runtime error, you fix it, you
>> continue.
>> 
>> This proposal does not groove me, it's syntactic sugar on top of a "Future
>> Closure".
> 
> I agree. In the beginning I was quite interested, but the more I think
> about it, the more I think this is not the right direction. I mean, yes,
> we cover the exceptional case, so there would be some benefit, but I do
> not feel it is enough.
> 
>> There is a need for async being easy in Groovy, but not based on a design
>> similar to completable future.
> 
> I agree
> 
>> Early erlang / early Go design, with a transparent management of the 
>> exception /
>> cancellation / timeout would be far better.
>> 
>> Something like, an async keyword to create an asynchronous expression and a
>> await + all/any and timemout should be enough
>> 
>> def captureTheFlag(hero, villain, flag) {
>>    var heroGrab    = async hero.grab(flag)        // a Task object with a 
>> volatile
>>    state typed State := NON_STARTED | Success(V value) | Failure(Throwable t)
>>                                                   // + an owner thread, so 
>> it can not be submitted/awaited by more than one thread
>>    var villainGrab = async villain.grab(flag)
>> 
>>    var winner = await 1s any(heroGrab, villainGrab) else "no winner"     // 
>> run
>>    using virtual threads or not (an Executor)
>>    
>>    print $heroGrab() // either the value or throw the exception thrown by
>>    hero.grab() (or a runtime exception for NON_STARTED)
>> }
>> 
>> The method captureTheFlag does not need to be flagged as "async", there is no
>> wrappring/rewriting to a completable future code
>> 
>> 
>> I think you should reconsider how to make async easy in Groovy, both in 
>> terms of
>> syntax but also in terms of what's happen at runtime.
> [...]
> I am aware that this is more aligned with the Go/Erlang design, but a
> couple of questions:
> I) I assume something like async{hero.grab(flag)} would be also ok. It
> does not have to be an expression. Similar for await.

Yes, it can be async { ... },
I am just afraid of people mutating local variables in the Closure.

  var inc = 0;
  var task = async { inc++ }
 
But you may say that because it does not solve the problem of people mutating a 
field anyway

  var box = new MutableInt(0);
  var task = async { box.value++ }  

An async { ... } is good enough ?


> II) Why no State RUNNING? In case heroGrab is shared between parallel
> processes I mean.
> III) Who is the owner thread going to be? 

If you are running in a parallel thread and you see that the state is RUNNING, 
you know nothing.
Because the parallel thread may have been de-scheduled just after reading the 
state and in between the worker thread may have finished.

It's the classical concurrency blurb:

  if (monitor.isLocked()) {
    // here the monitor may be not locked because reading that the state + 
executing the "if" is not atomic :(
  }

So reading the state from another parallel thread is usually a mistake, hence 
protecting the state with an owner thread check.

Once you can not read the state from another parallel thread, you do not need 
RUNNING anymore.
But maybe NON_STARTED is not the right name, it should be something like 
NOT_EXECUTED.


> Is it really the best idea to
> use some unnamed thread pool to create the thread... or be always
> virtual? That is also a question that bothers me in the current proposal.

It does not make a lot of sense if your are not using virtual threads.
But like the current proposal, if you target Groovy 6, you have to run on Java 
17,
so you need an emulation mode for Java 17.
...
or perhaps, it's better to wait Groovy 7 (or to throw an exception if Java 17 
on async {} but that does not sound good)

> 
> 
> bye Jochen

cheers,
Rémi

Reply via email to