and rust wasn’t mentioned!
“Impure” means the language design is by definition compromised. The moment you allow imperative behaviors to leak into a declarative system it loses its ability to reason reliably about operations over time; and you’re back to flying seat-of-your-pants a-la C &co. Haskell at least has the good grace to firewall any imperative crap so that the remainder of the program can still be reasoned about.
That doesn’t mean OCaml and F# can’t still be useful, in the same way that C is still useful, but they cannot be more than the sum of their own limitations; they are already evolutionary dead-ends. Thus article is correct not to expend any space considering them.
By your (imo, extremist) line of reasoning, wouldn't the existence of `unsafePerformIO` mean Haskell itself "looses its ability to reason reliably about operations over time"? I think a more reasonable position allows for gradations of reliability, and appreciates the benefits of encouraging pure computations while allowing room for judicious use of benevolent side effects.
> they are already evolutionary dead-ends
That is patently false, imo. OCaml has a number of interesting features which most statically typed FP languages are yet to explore (e.g., polymorphic variants, (typed) named and optional arguments, first-class modules, binding operators). More to the point, ongoing development on a novel approach to multi-core, native support for algebraic effects, and modular implicits, testify to it being a live evolutionary branch. OCaml aside, innovative languages and approaches like like F, 1Ml, Frank, BER MetaOcaml, and Rust seem to give clear evidence that we still have much to learn from impure* dialects of ML.
Well no, because for some reason, be it social or technical, people simply don't use unsafePerformIO in way that is actually unsafe.
> To the extent that the libraries and the ecosystem doen't make inappropriate use of unsafe behavior, you can have your impurity and reliable referential transparency too.
The key qualitative difference between Haskell and e.g. O'Caml/Scala/whatever here is that it's quite trivial to verify that your program doesn't use side effects inappropriately (aka. is not referentially transparent): Just make sure it contains no unsafeFoo calls. There's no such mechanism in O'Caml/Scala, etc. (I'm not sure, but I imagine it might even be undecidable unless you restrict to a teeny tiny subset of the language, e.g. just the pure arithmetic expression language.)
The qualitative difference in how confident you can be that moving a bit of code around won't change the semantics of your program is profound (or: bonkers) -- and before I'd experienced it myself I could have (rightly!) been extremely skeptical of such claims, yet here I am making that claim.
Yes, unless you really want to fully vet all of your transitive dependencies you will have quite a large TCB, but I find that it's actually really rare for libraries to use unsafeFoo willy-nilly. Some of the low-level stuff like Text, Vector, etc. do, but AFAICT it's mostly for performance reasons... and DGMW quite serious bugs have been found in these. However, because these are isolated data structures these bugs get fixed once and they are immediately fixed for all users.
Another wart wrt. a large TCB in Haskell is IO. Unfortunately, it has a very large surface area, but there's no really much that can be done about that at this point. Even still, as a programmer you can choose to use e.g. polysemy/fused-effects/etc. to define the semantics of effectful code in a much more fine-grained way such that the only point of contact between your code and IO is in a tiny "interpretation of semantics" layer.