John Carmack on mutable variables

Immutability by Default and Language Design

  • Many commenters agree with making variables immutable by default and requiring an explicit mutable/mut keyword.
  • They note several languages already do this or approximate it: Rust (let vs let mut), Swift (let/var), Kotlin/Scala/ML-family (val/var), F#, Dart (final), and Java/C# via final/readonly.
  • Others stress that in C/C++ a const-by-default switch is unrealistic due to backwards compatibility, though it could exist as a compiler flag or new “profile.”
  • There’s recurring confusion between immutable references and immutable data (e.g. JS/TS const vs Object.freeze, Java final vs truly immutable structures).

Functional Style in Mainstream Languages

  • Many see Carmack’s point as one more step in the long trend of importing functional ideas—immutability, pure functions, referential transparency—into imperative languages.
  • Functional-first but not pure languages (F#, Scala, Clojure, Gleam, OCaml, Rust) are held up as pragmatic middle grounds with escape hatches.
  • Several argue that pure FP is great “until it isn’t”: systems code, performance-sensitive hotspots, and “time-domain” programs often need controlled mutation.

Tooling, Debugging, and Code Clarity

  • A major benefit cited for const-everywhere is debugging: intermediate named values stay available and can’t be silently reused with different meanings after code is moved.
  • IDEs and linters already help: JetBrains products, Swift, TypeScript/ESLint, Clang-Tidy, Ruff, and Pylint can highlight mutated variables or suggest const/final.
  • Some prefer small, single-purpose functions and expression-based constructs (if/try returning values, pipelines) to reduce the need for mutable locals.

Trade-offs: Performance, Ergonomics, and State

  • Supporters emphasize easier reasoning, fewer hidden side effects, better thread safety, and smaller state space (“state is the enemy”).
  • Skeptics point to extra naming burden, verbosity, potential performance costs of copying, and friction in domains like GUIs, DSP, and real-time systems.
  • Several note compilers already use SSA and can often optimize immutable-looking code back into efficient mutations; structural sharing mitigates copying costs.

Ecosystem, Syntax, and Adoption

  • Functional languages face barriers: unfamiliar syntax (Haskell/Erlang/Lisp), fragmented tooling (historically Haskell, Lisp families), smaller job markets, and steeper learning curves.
  • Many developers prefer to “80/20” FP: mostly immutable and pure, but with clear, explicit escape hatches for mutation and I/O, rather than full purity.