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/mutkeyword. - They note several languages already do this or approximate it: Rust (
letvslet mut), Swift (let/var), Kotlin/Scala/ML-family (val/var), F#, Dart (final), and Java/C# viafinal/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
constvsObject.freeze, Javafinalvs 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/tryreturning 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.