I think you're moving the goalposts from "good to great FP support" to "is Haskell". I think that's the only one that fullfills both do-notation and tail call optimisation at the same time. Well, there's also Purescript and Scala.js running on JavaScriptCore since it has tail call optimisation, but that's a bit of a stretch. I see a lot of people like you that seem to have a very precise idea of what FP is supposed to be, and that idea always boils down to "basically Haskell". I think that's a fallacy, and that Haskell is not the only way to do FP. We have now languages like Koka, and soon (ish) OCaml that have effects, and are going to provide an alternative to Haskell's monads. There's also stuff like Scala with the DOT calculus. Lots of exciting stuff is happening in that space, and being a purist of one specific implementation isn't helping.
To get back to the subject of "good to great FP support", maybe we disagree fundamentally. Here are a few examples:
I would say that Rust is a good example of "great FP support". It's not a functional programming language, but it supports a lot of the patterns that make statically-typed functional programming languages effective. However, its DNA is still mostly "imperative programming language".
For "good FP support", I'd say TypeScript and C# are nice examples. C# has LINQ, pattern matching, records, optionals in a way. TypeScript has the usual JS stuff, plus the type system is nice. Both are lacking in some places. For example, in TypeScript/JavaScript, the usual map/reduce/filter are only functions on Array, and don't work with iterators.
To take the opposite view: what would be a functional programming language with good or great support for another paradigm? OCaml could be an example of good/great support for imperative programming. Scala has great functional programming and great object-oriented programming.
- pipe operator (or custom operators in general)
- do-notation
- tail call optimisation
- currying / partial application
- (better) type inference
- expression orientated
- less syntactic noise around function calls (fewer commas and parenthesis)
- type-classes or even runtime generic information to work around that
Some of the thing I’ve listed can never be added without dramatically changing what Java is. At that point it would be a new language.