Live data from Hacker News

John Carmack on mutable variables

twitter.com

371–380 of 663 posts

Re: John Carmack on mutable variables

#371

Earlier quoted context omitted.

whats the difference between immutable and constant, which has been in use far longer? why are you calling it mutable?

Immutable and constant are the same. rendaw didn't use the word mutable. One reason someone might use the word "mutable" is that it's a succinct way of expressing an idea. Alternative ways of expressing the same idea are longer words (changeable, non-constant).

but we already had the word variable for values that can change. on both counts it seems redundant

Re: John Carmack on mutable variables

#372
post #223

Earlier quoted context omitted.

In languages like JavaScript, immutable and constant may be theoretically the same thing, but in practice "const" means a variable cannot be reassigned, while "immutable" means a value cannot be mutated in place. They are very, very different semantically, because const is always local. Declaring something const has no effect on what happens with the value bound to a const variable anywhere else in the program. Where…

Arrays are a very notable example here. You can append to a const array in JS and TS, even in the same scope it was declared const. That’s always felt very odd to me.

That's because in many languages there is a difference between a stored reference being immutable and the contents of the thing the reference points to being immutable.

Re: John Carmack on mutable variables

#373

> ... making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword. Rust mentioned!

Here's a relevant comment on Rust: https://x.com/ID_AA_Carmack/status/1094419108781789184

That was 6 years ago. I'd like to see how that feeling developed.

Re: John Carmack on mutable variables

#374

Earlier quoted context omitted.

An order of magnitude? That sounds like pretty outrageous hyperbole. A variable getting reassigned 10 times sounds extremely rare, the average in my experience has to be less than 1 reassignment. I think the approach requires coming up with maybe 10% more names. Usually there are good, obvious names for intermediate calculations in my experience. I'm open though - what kinds of things are you doing that require reass…

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

Re: John Carmack on mutable variables

#375
post #321

Earlier quoted context omitted.

In the cases we're interested in here the variable does vary, what it doesn't do is mutate. Suppose I have a function which sums up all the prices of products in a cart, the total so far will frequently mutate, that's fine. In Rust we need to mark this variable "mut" because it will be mutated as each product's price is added. After calculating this total, we also add $10 shipping charge. That's a constant, we're (fo…

> In the cases we're interested in here the variable does vary, what it doesn't do is mutate. Those are synonyms, and this amounts to a retcon. The computer science term "variable" comes directly from standard mathematical function notation, where a variable reflects a quantity being related by the function to other variables. It absolutely is expected to "change", if not across "time" than across the domain of the f…

> Those are synonyms, and this amounts to a retcon.

The point is that it varies between calls to a function, rather than within a call. Consider, for example, a name for a value which is a pure function (in the mathematical sense) of the function's (in the CS sense) inputs.

Re: John Carmack on mutable variables

#376

> and it avoids problems where you move a block of code and it silently uses a version of the variable that wasn’t what it originally had. I find that keeping functions short also helps a ton with that. No, shorter than that. Short enough that the only meaningful place to "move a block of code" is into another function. Often, by itself.

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

Re: John Carmack on mutable variables

#377
post #259

Earlier quoted context omitted.

It forces you to consider when, where and why a change occurs and can help reason later about changes. Thread safety is a big plus.

Okay, so for example I might set something like "this bunch of parameters" immutable, but "this 16kB or so of floats" are just ordinary variables which change all the time? Or then would the block of floats be "immutable but not from this bit"? So the code that processes a block of samples can write to it, the code that fills the sample buffer can write to it, but nothing else should?

Sounds like you have a data structure like `Array`. The immutable approach has methods on Array like:

   Array append(Float value);
   Array replace(int index, Float value);
The methods don't mutate the array, they return a new array with the change.

The trick is: How do you make this fast without copying a whole array?

Clojure includes a variety of collection classes that "magically" make these operations fast, for a variety of data types (lists, sets, maps, queues, etc). Also on the JVM there's Vavr; if you dig around you might find equivalents for other platforms.

No it won't be quite as fast as mutating a raw buffer, but it's usually plenty fast enough and you can always special-case performance sensitive spots.

Even if you never write a line of production Clojure, it's worth experimenting with just to get into the mindset. I don't use it, but I apply the principles I learned from Clojure in all the other languages I do use.

Re: John Carmack on mutable variables

#378

Earlier quoted context omitted.

"const" means something can't be changed. "mutable" means it can be changed. You don't need both a "const" keyword and a "mutable" keyword in a programming language. You only need 1 of the keywords, because the other can be the default. munchler is saying the "const" keyword shouldn't exist, and instead all variables should be constant by default, and we should have a "mutable" keyword to mark variables as mutable. A…

> you don't need both a "const" keyword and a "mutable" keyword What if the lang has pointers? How express read-only?

compiler flags with line and column number seems like the easiest way

Re: John Carmack on mutable variables

#379
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?

> there still has to be mutable state somewhere - so where is it moved to?

This is one way of thinking about it: https://news.ycombinator.com/item?id=45701901 (Simplify your code: Functional core, imperative shell)

Re: John Carmack on mutable variables

#380
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?

In functional programs, you very explicitly _do not_ need to understand an entire program. You just need to know that a function does a thing. When you're implementing a function-- sure, you need to know what it does. But you're defining it in such a way that the user should not know _how_ it works, only _what_ it does. This is a major distinction between programs written with mutable state and those written without. The latter is _much_ easier to think about.

I often hear from programmers that "oh, functional programming must be hard." It's actually the opposite. Imperative programming is hard. I choose to be a functional programmer because I am dumb, and the language gives me superpowers.

Post reply on HN