Live data from Hacker News

John Carmack on mutable variables

twitter.com

441–450 of 663 posts

Re: John Carmack on mutable variables

#441
post #421

Earlier quoted context omitted.

Ah! Actually this idea that the immutable variables in a loop "change during execution" is a serious misunderstanding and some languages have tripped themselves up and had to fix it later when they baked this mistake into the language. What's happening is that each iteration of the loop these are new variables but they have the same name, they're not the same variables with a different value. When a language designer…

Seems like you're coming around to my side of the fence that calling these clearly distinct constant expressions "variables" is probably a mistake?

I don't think so? I've been clear that there are three distinct kinds of thing here - constants, immutable variables, and mutable variables.

In C the first needs us to step outside the language to the macro pre-processor, the second needs the keyword "const" and the third is the default

In Rust the first is a const, the second we can make with let and the third we need let mut, as Carmack says immutable should be the default.

Re: John Carmack on mutable variables

#442

Earlier quoted context omitted.

Probably exaggerated a bit with that phrasing (“outrageous” seems similarly hyperbolic ;)) But any variable which I’ve not already marked as const is pretty much by definition going to be modified at least once. So now instead of 1 variable name you need at least two. So now the average number of variables per non-const variable is >= 2 and will be much more if you’re doing for example DSP related code or other math…

Fair enough re: "outrageous"! It's actually math heavy code (or maybe medium heavy?) where I really like naming every intermediate. fov, tan_fov, half_tan_fov, center_x, norm_x

I spent a decade or so working on video codecs with an international team, and there was sort of an unwritten rule that code shouldn’t have comments and variable names shouldn’t be descriptive (english language couldn’t be assumed).

Which sounds really awful, but after a while it forces you to parse the logic itself instead of being guided by possibly-out-of-date comments and variable names.

I now prefer less verbosity so that probably explains why I’m a little out of distribution on this topic.

If you looked at any of my code prior to that job, it was the polar opposite with very “pretty” code and lengthy comments everywhere.

Re: John Carmack on mutable variables

#443

After a 2 year Clojure stint I find it very hard to explain the clarity that comes with immutability for programmers used to trigger effects with a mutation. I think it may be one of those things you have to see in order to understand.

Carmack is talking about variable reassignment here, which Clojure will happily let you mutate. For example: (let [result {:a 1} result (assoc result :b 2)] ...) He mentions that C and C++ allow const variables, but Clojure doesn't support that. clj-kondo has a :shadowed-var rule, but it will only find cases where you shadow a top-level var (not the case in my example).

That's not mutation though.

The `assoc` on the second binding is returning a new object; you're just shadowing the previous binding name.

This is different than mutation, because if you were to introduce an intermediate binding here, or break this into two `let`s, you could be holding references to both objects {:a 1} and {:a 1 :b 2} at any time in a consistent way - including in a future/promise dereferenced later.

Re: John Carmack on mutable variables

#444

Earlier quoted context omitted.

He’s implying that the variable it’s being defined within the loop. So, constant, but repeatedly redefined.

That's the opposite of what any reasonable engineer means by "constant".

No? It has a lifetime of one loop duration, and is constant during that duration. Seems perfectly fine to me.

Re: John Carmack on mutable variables

#445

Earlier quoted context omitted.

That's the opposite of what any reasonable engineer means by "constant".

That’s the point, you’re just haggling about scopes now. All the way from being new per program invocation to new per loop. Immutability doesn’t have this connotation.

How? I think the same argument applies: If it's changing from loop to loop, seems mutable to me.

Re: John Carmack on mutable variables

#446
post #185

Earlier quoted context omitted.

The way I like to think about is that with immutable data as default and pure functions, you get to treat the pure functions as black boxes. You don't need to know what's going on inside, and the function doesn't need to know what's going on in the outside world. The data shape becomes the contract. As such, localized context, everywhere, is perhaps the best way to explain it from the point of view of a mutable world…

Sure modularity, encapsulation etc are great tools for making components understandable and maintainable. However, don't you still need to understand the entire program as ultimately that's what you are trying to build. And if the state of the entire programme doesn't change - then nothing has happened. ie there still has to be mutable state somewhere - so where is it moved to?

[deleted]

Re: John Carmack on mutable variables

#447
post #83

Earlier quoted context omitted.

I use Swift for work. The compiler tell you this. If a mutable variable is never mutated it suggests making it non-mutable. And vice versa.

As will Typescript, at least using Biome to lint it does.

My very minor complaint about TypeScript is you use to use `const` which is 2 additional letters.

Seriously though, I do find it slightly difficult to reason about `const` vars in TypeScript because while a `const` variable cannot be reassigned, the value it references can still be mutated. I think TypeScript would benefit from more non-mutable values types... (I know there are some)

Swift has the same problem, in theory, but it's very easy to use a non-mutable value types in Swift (`struct`) so it's mitigated a bit.

Re: John Carmack on mutable variables

#448

Earlier quoted context omitted.

It helps with that, but it has other trade-offs: indirection isn't free for readability.

I'll go further than that and say that indirection significantly increases cognitive load and hurts readability.

I have consistently found the opposite to be the case across decades of programming experience, as regards the extraction of helper functions. This is not "indirection" in the same sense as with data structures. It is abstraction of currently-irrelevant detail.

Reading and understanding code is a process of answering "what are the immediate steps of this task?", without thinking about what those steps consist of or entail. It is not a process of answering "where is the variable representing ..., and the code that manipulates this?", especially since this makes assumptions that may prove incorrect.

Re: John Carmack on mutable variables

#449
post #185

Earlier quoted context omitted.

The way I like to think about is that with immutable data as default and pure functions, you get to treat the pure functions as black boxes. You don't need to know what's going on inside, and the function doesn't need to know what's going on in the outside world. The data shape becomes the contract. As such, localized context, everywhere, is perhaps the best way to explain it from the point of view of a mutable world…

Sure modularity, encapsulation etc are great tools for making components understandable and maintainable. However, don't you still need to understand the entire program as ultimately that's what you are trying to build. And if the state of the entire programme doesn't change - then nothing has happened. ie there still has to be mutable state somewhere - so where is it moved to?

It lets you refine when and where it happens more than other methods of restricting state change, such as in imperative OOP.

Re: John Carmack on mutable variables

#450

Earlier quoted context omitted.

Carmack is talking about variable reassignment here, which Clojure will happily let you mutate. For example: (let [result {:a 1} result (assoc result :b 2)] ...) He mentions that C and C++ allow const variables, but Clojure doesn't support that. clj-kondo has a :shadowed-var rule, but it will only find cases where you shadow a top-level var (not the case in my example).

That's not mutation though. The `assoc` on the second binding is returning a new object; you're just shadowing the previous binding name. This is different than mutation, because if you were to introduce an intermediate binding here, or break this into two `let`s, you could be holding references to both objects {:a 1} and {:a 1 :b 2} at any time in a consistent way - including in a future/promise dereferenced later.

[deleted]
Post reply on HN