Loom will present an ergonomic alternative for concurrency that is strictly superior to futures, but it will not replace or significantly simplify RxJava[1].
Project Loom is also orthogonal to actors - it really doesn't dictate any specific concurrency model, it just provides fiber support in the JVM. We will definitely see actor libraries once project loom is released, but these are already available now, and they are not widely used. The only thing Loom can help them with (I hope) is better performance, but I don't think that's the reason the actor model lost ground in the Java world.
[1] Project Loom can be the basis for some minor simplifications, as JetBrains did in kotlinx.coroutines.flow.
For example, this is a Hello World API in Groovy (this is almost literally the entire Application):
@Controller('/hello')
class HelloController {
String index() {
'Hello World'
}
}get("/", (req, res) -> "hello world")
Is just as clear to me.
Not saying micronaut doesn't have things to offer, but simplicity of setting up an http endpoint isn't unique.
They were also not invented by Java, other languages like Dylan, Python and .NET actually go them first.
Actually they are like a poor man's Lisp macros kind of.
Best description of annotations I read. Literally this, a lot of times I figure an annotation is way simpler than fighting the generic system and all it's flaws.
https://www.nongnu.org/txr/txr-manpage.html#N-00B4065C
An example is given of a memoization parameter macro that you invoke simply by adding :memo to the front of the parameter list, as in (lambda (:memo ...) ...). This inlines everything; the function itself rewritten to do the caching with open code, rather than wrapped with a caching function.
TXR Lisp's support for keyword arguments is also implemented as a parameter macro, using the documented define-param-expander public interface.
http://www.kylheku.com/cgit/txr/tree/share/txr/stdlib/keypar...
This is possible because the parameter macro can introduce identifiers into the scope of the function, and rewrite the parameter list.
And rejoice yourself, Kotlin Flow/coroutines has a clean API that feels synchronous and that still manage to be reactive streams compliant! It's mostly perfect and project loom will be the ice on the cake! Others platforms (non JVM) really have a lot to learn in this regard
I've done some vert.x and creating new Futures all the time is a bit cumbersome, but I understand it. With RxJava it's another layer on top, and you are even more removed.
The whole non-blocking experience in java feels like there is a lot of plumbing. I know it's fast for IO reasons, but it really feels like the JVM/CPU has to jump through a lot of glue code to make it work.
We solve this problem with APIs for asynchronous or nonblocking IO.
But such APIs must be cleverly designed if they are to permit you to propagate backpressure from the downstream end of your program's dataflow to the upstream end. And handle errors in a sane way, etc.
You're wasting resources when your application is already in a state where it knows it won't be able to handle the request. Eventually the memory taken by the partially processed requests is going to exceed what you can take in (unless you cap the number of concurrently processed requests, which is also an inelastic backpressure of sorts) and the service will crash.
What you mentioned is decent for inelastic blocking synchronous processing (you can have at most X concurrent requests, because that's how many threads for processing you've configured based on performance tests and production monitoring), but you can relatively easy fill in an internal queue somewhere if it's async.
The real advantage the reactive model offers is a more powerful abstraction for data flow than futures. I found it useful than straight coroutines or async/await in cases where I had to process complex data flows or combine periodically polled data from multiple sources, but in well over 99% of the use cases for an async REST API it's an overkill.
Removing the ceremony around asynchronous I/O, on the other hand, requires language support, so your best option right now is to just use Kotlin coroutines. Or wait for Project Loom, but I assume it's at least 2 years away from a stable release.
You're mostly stuck to Rx outside Kotlin, tho.
Use it only if you really really have to, then check if you can't do it in Kotlin instead with native async/await syntax, and only if the answer to those questions is "no", continue to shoot yourself in the foot.