I compare async/await with manual threading, or with callback hell, and in both cases it is a great improvement. I haven't tried Java with Loom and haven't done much in Go, so maybe that style is indeed better. Right now it feels spooky if my lightweight thread can suspend at any time, deep down in some function call. And what if you accidentially don't call a "loomified" IO function, but a plain blocking one? I like the explicitness of "cooperative" await but agree it can be a bit tedious. Maybe there is a better way of integrating "asyncness" in the type system?
Yeah the last idea came when I added undo/redo to an app. I had to go mechanically through everything and make every change to my model go through a "Command" subclass. When I encounter something like this I think "why can't the compiler do this for me?". Probably solvable by LISP macros, or transactions as you said.