💾 Archived View for yujiri.xyz › software › why-not-functional.gmi captured on 2024-08-18 at 17:23:32. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
My first contact with pure functional programming was Haskell. The idea sounded bizarre, but fascinating. I had faith that once I'd learned Haskell, I'd understand how to use things like monads to replace the uses of state and side effects.
But that wasn't what happened. After reaching a medium level in Haskell, what I learned was that although the functionality of state and side effects could be replicated, it was only in the sense that everything can be replicated in assembly. Even with all the elaborate abstractions, state would never be *easy*. It was a huge weakness of functional programming that would plague almost any big project.
And I'm not blaming the paradigm for the flaws of a language. Most of my gripes with Haskell aren't the fault of pure functional programming (Idris is a remake that solves most of them), but this one is: state becomes unreasonably difficult to represent. Not 'difficult' in the sense that it was still hard to figure out once I understood the concepts, but that it was a lot of work to make something impure. Making a function deep in the logic log or be affected by an environment variable required everything below it in the stack to be refactored. Using multiple monads together required monad transformers which meant comically convoluted type signatures (which you would alias) and a sprinkling of *glue* functions like `liftIO` and `runWriterT`.
Are there good things about pure functional programming? I'm not sure. You probably expected me to say yes, because we're always expected to hedge like that when we make extraordinary claims. But I'm not sure because I've seen how every other good idea in Haskell can be had without banning side effects. The main demonstration is Rust.
Rust was influenced by the ML family (the same family that Haskell comes from). It has algebraic data types and a constraint system for type parameters. That lets it implement `map`, `filter`, comprehensions (`filter_map`), `zip`, monadic short-circuiting for the error-handling sum type, and other hallmarks of functional programming, with as much type safety and code reuse as any functional language can offer.
But Rust is an imperative language. And the nail in the coffin is this: *everything is immutable by default*. Rust still makes mutation explicit. And I think that provides most, maybe all, of the benefits of pure functional programming.
What is Rust missing compared to Haskell and Idris?
And that's why I don't believe in pure functional programming anymore.