First of all, what makes F # powerful, in my opinion, is not just the default immutability, but a whole set of functions such as: default immutability, type inference, easy syntax, sum (DU) and product types (tuples) , pattern matching and currying by default. Perhaps more.
They make F # very functional by default, and they make your program a certain way. In particular, they make you uncomfortable when you use a mutable state, as this requires the mutable keyword. Inconvenient in this sense means more careful. And thatβs exactly what you should be.
A volatile state is not forbidden or evil per se , but it must be controlled . The need to explicitly use mutable is like a warning sign warning you of danger. And good ways to manage it use it internally within a function. This way you can have your own internal mutable state and still be completely thread safe because you don't have a shared mutable state . In fact, your function may still be transparent, even if it uses the changed state internally.
As to why F # allows a volatile state; it would be very difficult to write regular real-world code without the ability for it. For example, in Haskell, something like a random number cannot be done in the same way as it can be done in F #, but rather requires streaming through the state explicitly.
When I write applications, I usually have about 95% of the code base in a very functional style that would be pretty much 1: 1 portable to say Haskell without any problems. But then a critical state is used at the boundaries of the system or in some critical critical internal circuit. Thus, you get the best of both worlds.
source share