135 comments

[ 5.2 ms ] story [ 106 ms ] thread
> Making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword.

It's funny how functional programming is slowly becoming the best practice for modern code (pure functions, no side-effects), yet functional programming languages are still considered fringe tech for some reason.

If you want a language where const is the default and mutable is a keyword, try F# for starters. I switched and never looked back.

> If you want a language where const is the default and mutable is a keyword

whats the difference between const and mutable?

I get your sentiment, but I side on it's infuriating that it's taken this long. Lol.

F# is wonderful. It is the best general purpose language, in my opinion. I looked for F# jobs for years and never landed one and gave up. I write Rust now which has opened up the embedded world, and it's nice that Rust paid attention to F# and OCaml.

It’s because FP, great as it is, is most beneficial when 80/20’d.

Trying to do it 100% pure makes everything take significantly longer to build and also makes performance difficult when you need it. It’s also hard to hire/onboard people who can grok it.

I want a language that helps me write software. I do not need a language that's hellbent on expressing a particular ideology.
> I wish it was the default, and mutable was a keyword.

I wish the IDE would simply provide a small clue, visible but graphically unobtrusive, that it was mutated.

In fact, I end up wishing this about almost every language feature that passes my mind. For example, I don't need to choose whether I can or can't append to a list; just make it unappendable if you can prove I don't append. I don't care if it's a map, list, set, listOf, array, vector, arrayOf, Array.of(), etc unless it's going to get in my way because I have ten CPU cores and I'll optimize the loop when I need to.

Years ago I did a project where we followed a lot of strict immutability for thread safety reasons. (Immutable objects can be read safely from multiple threads.)

It made the code easier to read because it was easier to track down what could change and what couldn't. I'm now a huge fan of the concept.

Immutability was gaining huge traction with Java... then large parts of the industry switched to golang and we hardly make anything immutable.
[flagged]
I hope this experimental language becomes usable eventually, all I hear is how you have to continuously rewrite to work around the borrow checker, accept worse performance to make everything message based, or just ignore the type system and write VB6-style code where everything is indexes in arrays to obfuscate ownership and paste 'unsafe' when it moans.
How fast this got to the top, you would think John Carmack just invented nuclear fusion.
How many hours of discussions went into topics like code formatting and naming things like variables, endpoints, classes, etc?
I was part of the Carmack cult but the illusion was broken when I saw him use the same authoratative tone on a subject I'm more knowledgable about.
People worship this guy, but other than being a good C++ graphics programmer, it isn't clear what he's actually done.
Right? This isn't even a hot take - it's just standard software engineering advice we all learn in school or on the job.
Why loops specifically? Why not conditionals?

A lot of code needs to assemble a result set based on if/then or switch statements. Maybe you could add those in each step of a chain of inline functions, but what if you need to skip some of that logic in certain cases? It's often much more readable to start off with a null result and put your (relatively functional) code inside if/then blocks to clearly show different logic for different cases.

[flagged]
Proposing making immutable by default in C or C++ doesn't make sense due to backwards compatibility reasons. New languages like Rust have easier time making better choices with immutable by default.
Could be a compiler flag. -const-by-default. Would probably mean you need to scatter mutable across the codebase to get it to compile, but I expect some people would like to have every local annotated as const or mutable.
Maybe the new C++ profiles that are supposedly going to make C++ a safe language could do it.
Kinda curious on what jblow would say about this.
Yeah, I also wish it was the default. But it's a little too verbose to just sprinkle on every variable in C++. Alas. Rust gets this right, but I'm stuck with C++ at work.
100%, life's too short.

Ultra-pedantic const-correctness (vs tasteful const-correctness on e.g. pass-by-reference arguments or static/big objects) catches nearly no bugs in practice and significantly increases the visual noise of your code.

If you have luxury of designing a new language or using one with default mutability then do so, but don't turn C coding styles into C++-envy, or C++ coding styles into Rust-envy.

> But it's a little too verbose to just sprinkle on every variable in C++

It's worth it, though. Every variable that isn't mutated should be const. Every parameter not mutated should be const, and so should every method that doesn't mutate any fields. The mutable keyword should be banned.

[flagged]
In JavaScript, I really like const and have adopted this approach. There are some annoying situations where it doesn't work though, to do with scoping. Particularly:

- if (x) { const y = true } else { const y = false } // y doesn't exist after the block - try { const x = foo } catch (e) { } // x doesn't exist after the try block

JavaScript’s `const` has the bigger issue that while things can’t be reassigned, they can still mutate. For example:

  const myArray = [1,2,3]
  myArray.push(4)
  myArray // [1, 2, 3, 4]
Constants are useful for reasoning about code, but anyone who focuses only on making everything an immutable is missing the point. The main goal should be achieving referential transparancy.

It can be perfectly fine to use mutable variables within a block, like a function when absolutely needed - for example, in JavaScript's try catch and switch statements that need to set a variable for later use. As long as these assignments are local to that block, the larger code remains side-effect free and still easy to reason about, refactor and mantain.

https://rockthejvm.com/articles/what-is-referential-transpar...

Rediscovering one of the many great decisions that Erlang made
Yeah I wish variables were immutable by default and everything was an expression

Oh well continues day job as a Clojure programmer that is actively threatened by an obnoxious python take over

Clojure will always be faster than Python. So you have that, at least.
Rust taught me that a language does not have to be purely functional to have everything be an expression, and ever since I first used Rust years ago I've been wishing every other language worked that way. It's such a nice way to avoid or limit the scope of mutations
You are not a Clojure programmer. You use Clojure to solve problems in a professional context. I'm sorry that there's a political tribal war based on language going on at your workplace.

But especially now that coding agents are radically enabling gains in developer productivity, you don't need to feel excluded by the artificial tribal boundaries.

If you haven't, I recommend reading: https://www.kalzumeus.com/2011/10/28/dont-call-yourself-a-pr...

Is there a ruff rule for this?
There are [1] and [2] for function arguments and loop variables, respectively, but nothing for the general case. Note that a type checker will complain if you re-assign with a different type. Pylint also has [3] for redefining variables from an outer scope, but Ruff doesn't implement that yet.

[1] https://docs.astral.sh/ruff/rules/redefined-argument-from-lo...

[2] https://docs.astral.sh/ruff/rules/redefined-loop-name/

[3] https://pylint.pycqa.org/en/latest/user_guide/messages/warni...

Variable is by definition mutable.

Constant is by definition immutable.

Why can't people get it through their heads in 2025? (I'm looking at you, Rust)

On a similar note, I’ve always liked the idea of being able to mark functions as pure (for some reasonable definition of pure).

The principle of reducing state changes and side-effects feels a good one.

> In C/C++, making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword.

I know it's irrelevant to his point, and it's not true of C, and it doesn't have the meaning he wants, but the pedant in me is screaming and I'm surprised it hasn't been said in the comments:

In C++ mutable is a keyword.

When I started programming in Haskell, where all variables are immutable, I felt like I was in a straitjacket

Then, suddenly, the enlightenment

How do you know it’s real? When one is in a straitjacket for a long time, the trauma might make the mind disassociate from the body, giving an illusion of freedom.

The brain is essentially dreaming it’s escaped while the body is still programming in Java.

How does Haskell deal with things like memory-mapped I/O or signal handlers where the value of a variable can be changed by factors outside of the programmer's control?
The lovely thing with Haskell is you have the ST monad that lets you have as many mutable variables as you want, as long as they stay within ST.
This is almost standard in modern languages:

For example in Dart you make everything `final` by default.

A real default doesn’t need a keyword.