In practice, despite the features Java can support, it's typically too much work to define a new type for safety reasons. Strings are used everywhere, instead of more domain specific types. Even the designers made compareTo return an Int!
In practice, despite the features Java can support, it's typically too much work to define a new type for safety reasons. Strings are used everywhere, instead of more domain specific types. Even the designers made compareTo return an Int!
Since Haskell's type system is still more powerful than what is achievable with the .NET CLR, I'd assume this effect to be even stronger there.
It's really amazing how far sane language defaults can get you. In C#, I encounter badly designed types all the time. The main reason for this is not that it is impossible to design them right, but that the language makes it so tedious. I'm also really longing for the new features in C# 8, in the hope that they manage to remedy some of that (record types, pattern matching and non-nullable references sound like a good start).
My experience in coding functionally has shown that mutation (especially if localized and doesn't leak outside of a function) can really help functional code become a lot faster. From what you describe C# is slowly becoming an F# clone; which may be a good thing or not. Still think that if you need these features you should move to Scala/F#/Ocaml/Haskell though since I suspect these features will be added to C# in a clumsy/compromise way given its roots (e.g Scala and F# already have exhaustive pattern matching, async streams/iterators, better type inference, etc.).
100% agreed, with one caveat: You usually don't get to go "Boss, we're using F# now, 'kay?"
Some workplaces are sadly very constricted in those terms. Maybe I'll see the day when my team gets to use a more functional language, but until then, I'll take what I can get.
There's this magical thing in Haskell called the "ST Monad", where you can have a pure function (does not have IO in its type signature, does not use unsafePerformIO) that takes a normal value, returns another value, but can use mutation inside the function, and the type system guarantees that the mutation doesn't "leave" the function. So for those cases where, in C++ I would think "this function is pure enough, it doesn't print or order fish food, though it does mutate this temporary array", Haskell's compiler will actually confirm for you "yeah, you're right, it is pure enough".