Well, yeah. Haskell is a research language, while Java's stated design philosophy from day one has been to be conservative with adding new features, and judiciously add new features after they've proven useful in other languages.
So how does that make a difference to my point? The title is literally false as multiple other languages have already done it, Haskell in fact has even rewritten the underpinnings at least once the feature has been there so long.
The fact that X exists doesn't mean the "era of X" has started yet, you have to have the exponential adoption curve. The "Internet Age" started several years after the Internet was created
And clearly, adding this feature to Java is going to be like putting a web browser in Windows 95
Are you sure? All the discussion I can find online makes it seem to me like TPL and friends are just executing tasks on thread pools until completion. (see e.g., https://github.com/dotnet/runtime/issues/50796 for some discussion)
I don't think this is the same thing. As far as I can tell, the task abstraction is a threapool where you can submit operations and return futures. If a task blocks indefinitely, the underlying threadpool OS worker thread will be blocked, and the threadpool either has to run with less resources or spawn a new worker. Virtual threads are an M:N abstraction: blocking on a virtual thread will not block the underlying OS thread.
.NET might indeed have a virtual thread abstraction and if it does you could of course implement the Task abstraction on top of either virtual threads or OS threads, but what you linked to is not a proof that it does.
That looks similar to Java FutureTasks + Executors which is a very different concept from virtual threads.
Virtual threads mean that a blocking thread can yield to any other non blocking thread seamlessly and with very little overhead. .NET Tasks cannot do this as far as I can tell.
Oh interesting, that's very cool, I didn't realize Java was doing that. That's a different axis than M:N though (cooperative versus preemptive) and you could definitely write a preemptive async runtime for Rust (rtic comes to mind). But the async-std and tokio runtimes are certainly cooperative.
(As a note, cooperative scheduling also requires a runtime - Rust might not "have a runtime" by default but you need to opt into one to use async.)