OTOH, It does seem like magic if you have dealt with Java in your previous life
Having worked with Rails codebases I tend to avoid magic when not needed.
The best effect is using FP, then going back to what you used before and adopting more FP style: single assignment to local vars, immutable datastructures, less imperative control flow, etc.
- Discriminated unions and pattern matching
- Global type inference
- Tail call optimisation
- Syntactic sugar for monadic code (computation expressions)
Java is more “FP” than it used to be, but it is still not minimum viable FP
For sum types, the syntax is like this:
sealed interface Expression permits Add, Mult, Identifier {}
// possibly later
record Add(Expression a, Expression b) extends Expression {}
record Identifier(String name) extends Expression {}
I do like global type inference, but for anything that gets committed I often come to prefer at least Rust’s restriction of top level definitions needing types, as that aligns well with how I would also write Haskell and alia. Java imo made a good decision of only adding optional local inference, anything else would have been too disruptive for Java, given its existing talent pool’s background.