Functional languages should be so much better at mutation than they are

Mutability in ML-family and other FP languages

  • Several comments argue the article mischaracterizes OCaml/ML: mutation is well-supported and used when it’s the best tool, especially for arrays and unboxed floats.
  • Preference for immutable code is framed as trusting the compiler and GC to optimize, not fear of mutation.
  • F# is cited as a pragmatic example: mostly functional style, but mutability is common at boundaries and for performance.

Haskell, purity, and ergonomics

  • Multiple posters see the article as really about Haskell’s design: strict segregation of effects, monadic I/O, and laziness.
  • Critiques: poor performance on real hardware (space leaks, indirection), awkward everyday mutation patterns (State, ST, lenses), and steep learning curve for “big-tech” engineering.
  • Others defend Haskell’s purity and monadic approach as giving strong semantic guarantees; the real question is whether the cost is worth it.

Monads, ST/State, STM, and effect systems

  • Some say the article underestimates ST, State, and STM; these are widely used to implement mutable arrays, hash maps, and concurrent mutations in a controlled way.
  • Objections to ST focus on difficulty mixing with other effects; monad transformers help but have ordering and boilerplate issues.
  • Effect systems like Bluefin and algebraic effects are mentioned as ways to compose state, exceptions, streams, and I/O more flexibly.

Linear / uniqueness types and Rust-style ownership

  • Distinction is drawn between linear types (“must use exactly once”) and uniqueness types (“must be unaliased to mutate”).
  • Rust is presented as largely solving local mutability by requiring exclusive, non-aliased access for mutation; this prevents “spooky” shared updates.
  • Clean, Granule, Roc, Koka, and planned OCaml extensions explore uniqueness/linearity to enable safe in-place updates and reuse.

Copy-on-write, reference counting, and compiler optimizations

  • Copy-on-write with reference counting is common (Swift, Tcl, Roc, jq); it can fall into quadratic behavior when refactoring introduces extra aliases.
  • Some argue static analysis (SSA, uniqueness) can often avoid runtime reference counts and enable in-place mutation under the hood.
  • Koka’s FBIP optimization and “one-bit reference counts” for tracing GCs are discussed as hybrids.

Lists vs arrays and performance

  • Debate over whether idiomatic list-based OCaml is “as fast as” mutable arrays.
  • Microbenchmarks show hand-written dynamic arrays can outperform lists significantly; OCaml’s built-in Dynarray trades speed for stronger guarantees.
  • Tail-call optimization, “tail-mod-cons”, and GC behavior (minor heap optimized for immutability) complicate simple claims about arrays always winning.

Exceptions vs Result-style errors

  • Discussion contrasts exceptions with sum-type results (Result/Either).
  • Points raised: exceptions propagate silently and usually carry stack traces; Result forces explicit handling but often loses the creation-site trace.
  • In Haskell, runtime exceptions exist alongside Either; laziness makes exception semantics subtle and handling from pure code impossible.

Garbage collection vs reference counting

  • Strong back-and-forth: some claim tracing GC is “massively worse” than ref counting; others counter that modern tracing GCs usually beat RC on throughput.
  • Trade-offs are laid out: GC needs more memory but can make allocation/free cheap; RC imposes overhead on every pointer update and struggles with cycles.
  • Real-time and concurrent GCs, arena allocators, and fragmentation are cited as key design dimensions.

Broader views on FP and mutation

  • Several comments reframe FP as managing, not eliminating, mutation: aim for a functional core with imperative/mutable “shells.”
  • Others are skeptical of FP as a whole, arguing many of its benefits can be achieved with message-passing, encapsulation, and disciplined OO.
  • There is general agreement that mutation is necessary; the central question is how languages and type systems help constrain and reason about it.