Java can put you off with all the boiler plate. That's not a requirement of static typing.
Java can put you off with all the boiler plate. That's not a requirement of static typing.
F# skirts-around some of the limitations with just-in-time design-time type generation ( https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/typ... ) though Microsoft pitches it as a "better T4" for F# specifically, I think it's far more powerful.
The way things are looking, C# is set up get some form of higher-kinded-types in the next 3-5 years (hurrah!) - but from what Mads and the design-team have said it seems like they'll be implemented without significant changes to the CLR's type-system - so be prepared for everything to be implemented as glorified structs (and don't expect struct inheritance either...) with excessive amounts of implicit-conversion.
The feature you state (F# unions) - that simplification has some positives as well. I can exhaustive pattern match for example. I can always use composition/pattern matching/active patterns to mix in other assemblies types cases. Is it really limiting that it doesn't do this? Would changing this mean more complexity and make other language features harder to deliver? All features introduce some additional complexity, and it more than linear typically. F# also has some interesting patterns that were either introduced first there en masse (e.g Async) or are easier to use there.
I'm a fan of static checking, and conciseness with performance close to the platform it is hosted on and is easy to reason about. I'm also a fan of easy to read and clean code that isn't "scary" to look at. F# seems to strike that balance well compared to other JIT functional languages, at least from where I’m sitting. Too much complexity and you start scaring people away and/or too much abstraction in the codebase starts to occur trading off maintainability.