1) default immutability (same simple data structures used in every library -> ecosystem composes better)
2) portable code across multiple host platforms (jvm, node, browser)
3) metaprogramming
IMO, in 2007, immutability on the JVM was a competitive value prop, but in 2021+ it is nothing special. It is the combination of the three things which is a competitive value prop today. Metaprogramming in particular is a mostly unexplored frontier, because it is very hard to do well, and very easy to do badly. Default immutability is kind of a necessary starting point to do metaprogramming well.
immutability as a library is available in basically all mainstream languages now and mainstream frameworks leverage it (react, any UI framework, spark, any data framework or database); JS vms are competitive with the JVM and the JVM might even be losing ground in the cloud; typescript is a monster and is letting people explore haskell concepts in industry applications; scala is way better in 2021 than it was in 2011; every new PL can compile to JS and supports immutability. Clojure's sweet spot is currently developing sub-languages embedded in Clojure like RPL is doing (we are doing exactly the same thing at hyperfiddle). That is still too hard to do in typescript imo. Maybe it is possible in scala 3 but it took them 10 years of pure autism to figure out how to do monadic IO in scala due to how complex scala is, so i'd expect it to take another 10 years to figure out how to do metaprogramming in a commercially viable way, I'd be happy to be proven wrong.
As far as the JVM goes only Clojure and Scala have real immutability built-in, ie. persistent data structures. Kotlin's default immutability is more like read-only.
I think Clojure is still a great choice when your concurrent solution to a problem is amenable to working with stale data. If it is then the correctness of your design will rely heavily upon immutability, and the bugs that can be introduced by not using an immutable-first language like Clojure just isn't worth the risk.
I think that's also why Clojure use peaked right before Java 8 was released; once Java became a better Java, and libraries started to be designed around more ergonomic APIs than wiring up objects that needed miles of stateful configuration code, the pressure that drove you out of Java and into Clojure began to diminish.
Hardly. Despite being Java-compatible Clojure's philosophy is diametrically opposed to that of Java/OOP so anyone who has experienced the benefits of Clojure would hardly return to Java simply because it managed to fake FP with Single Abstract Methods.
Well, maybe there were not as many die-hard lispers within the Clojure community and many were looking for something that handled better than Java 7. I know two other former Clojure programmers that went to Scala, but also find Java passable enough to just use it rather than Clojure.
Interesting take since Clojure and Java are two very different languages. And unlike for example Kotlin, Clojure does not try to be a better Java than Java. But true though Clojure leverages the power of the jvm.
The doto macro was a relief back in the days when Java APIs insisted on being designed around stateful setters and getters rather than the builder pattern, it allowed you to operate on such unfortunately designed objects in single logical blocks and simulating it all being an expression.
Proxy allowed you to instantiate anonymous inner classes implementing only the methods of an interface that you needed, the rest you could omit; in Java you have to put them all, empty, which necessitated that you use an IDE to generate them, and back then IDEs were not as nice as today.
Those two alone made interacting with contemporary Java libraries so much easier.
It also was convenient to go from Java collections to Clojure collections and vice versa.
Interop from Clojure -> Java is still incredibly easy, though. I often will just wrap a Java library rather than look for a pre-existing Clojure implementation because it's so easy. Of course, most Java libs don't value immutability and functional patterns, but you can still push this kind of interop to the edges of your system and keep everything else in pure Clojure.
Depends on the style the Java library you are trying to use is written in. I have had mixed results. Especially the Java 8 functional style had some challenges. I might have the relevant SO question somewhere.