Earlier quoted context omitted.
Or between iterations of the loop scope in which it's defined, const/immutable definitions absolutely change during the execution of a function. I understand the nitpicky argument, I just think it's kinda dumb. It's a transparent attempt to justify jargon that we all know is needlessly confusing.
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…
John Carmack on mutable variables
421–430 of 663 posts
Re: John Carmack on mutable variables
#422Earlier quoted context omitted.
I think the explanation is: When you mutate variables it implicitly creates an ordering dependency - later uses of the variable rely on previous mutations. However, this is an implicit dependency that isn't modeled by the language so reordering won't cause any errors. With a very basic concrete example: x = 7 x = x + 3 x = x / 2 Vs x = 7 x1 = x + 3 x2 = x1 / 2 Reordering the first will have no error, but you'll get t…
Yet even Rust allows you to shadow variables with another one with the same name. Yes, they are two different variables, but for a human reader they have the same name. I think that Rust made this decision because the x1, x2, x3 style of code is really a pain in the ass to write.
Re: John Carmack on mutable variables
#423After 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).
I think in practice this is the ideal middle ground of convenience (putting version numbers at the end of variables being annoying), but retaining mostly sane semantics and reuse of prior intermediate results.
Re: John Carmack on mutable variables
#424I try to keep deeper mutation to where it belongs, but I'll admit to shadowing variables pretty often. If I have a `result` and I need to post-process it. I'm generally much happier doing `result = result.process()` rather than having something like `preresult`. Works nicely in cases where you end up moving it into a condition, or commenting it out to test an assumption while developing. If there's an obvious name fo…
Yes, but there are often FP tricks and conveniences that make this unnecessary. Like chaining or composing function calls. result = x |> foo |> bar |> baz (-> x foo bar baz) Or map and reduce for iterating over collections. Etc.
Re: John Carmack on mutable variables
#425I completely agree with the assertion and the benefits that ensue, but my attention is always snagged by the nomenclature. I know there are alternate names available to us, but even in the context of this very conversation (and headline), the thing is being called a "variable." What is a "variable" if not something that varies?
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…
Re: John Carmack on mutable variables
#426Earlier quoted context omitted.
I think the explanation is: When you mutate variables it implicitly creates an ordering dependency - later uses of the variable rely on previous mutations. However, this is an implicit dependency that isn't modeled by the language so reordering won't cause any errors. With a very basic concrete example: x = 7 x = x + 3 x = x / 2 Vs x = 7 x1 = x + 3 x2 = x1 / 2 Reordering the first will have no error, but you'll get t…
Yet even Rust allows you to shadow variables with another one with the same name. Yes, they are two different variables, but for a human reader they have the same name. I think that Rust made this decision because the x1, x2, x3 style of code is really a pain in the ass to write.
let x = "29"
let x = x.parse::()
let x = x.unwrap()
These all use the same name, but you still have the same explicit ordering dependency because they are typed differently. The first is a &str, the second a Result, the third an i32, and any reordering of the lines would provide a compiler error. And if you add another line `let y = process(x)` you would expect it to do something similar no matter where you introduce it in these statements, provided it accepts the current type of x, because the values represent the "same" data.Once you actually "change" the value, for example by dividing by 3, I would consider it unidiomatic to shadow under the same name. Either mark it as mutable for preferably make a new variable with a name that represents what the new value now expresses
Re: John Carmack on mutable variables
#427Earlier quoted context omitted.
It also doesn't make sense for `process()` to be an attribute of `result`. Why would you instantiate a class and call it result‽
A more common example for me at work is getting a response from url. Then you gotta process it further like response.json() or response.header or response.text etc etc. and then again select the necessary array index or doc value from it. Giving a name like pre_result or result_json etc etc would just become cumbersome.
Re: John Carmack on mutable variables
#428I completely agree with the assertion and the benefits that ensue, but my attention is always snagged by the nomenclature. I know there are alternate names available to us, but even in the context of this very conversation (and headline), the thing is being called a "variable." What is a "variable" if not something that varies?
(let [a 10] a)
Let the symbol `a` be bound to the value `10` in the enclosing scope.
Re: John Carmack on mutable variables
#429Earlier quoted context omitted.
I think the explanation is: When you mutate variables it implicitly creates an ordering dependency - later uses of the variable rely on previous mutations. However, this is an implicit dependency that isn't modeled by the language so reordering won't cause any errors. With a very basic concrete example: x = 7 x = x + 3 x = x / 2 Vs x = 7 x1 = x + 3 x2 = x1 / 2 Reordering the first will have no error, but you'll get t…
whats the difference between immutable and constant, which has been in use far longer? why are you calling it mutable?
> why are you calling it mutable?
Mostly just convention. Rust has immutable by default and you have to mark variables specifically with `mut` (so `let mut var_name = 10;`). Other languages distinguish between variables and values, so var and val, or something like that. Or they might do var and const (JS does this I think) to be more distinct.
Re: John Carmack on mutable variables
#430I try to keep deeper mutation to where it belongs, but I'll admit to shadowing variables pretty often. If I have a `result` and I need to post-process it. I'm generally much happier doing `result = result.process()` rather than having something like `preresult`. Works nicely in cases where you end up moving it into a condition, or commenting it out to test an assumption while developing. If there's an obvious name fo…
You're using really generic terms which I have to think is mostly because you're talking about it in the abstract. In most scenarios I find there are obvious non-generic names I can use for each step of a calculation.