Live data from Hacker News

John Carmack on mutable variables

twitter.com

391–400 of 663 posts

Re: John Carmack on mutable variables

#391
post #157
post #124

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

I've had this experience with going from PHP and JS to typed languages. Many years ago I was a type sceptic, but after being forced to use them, I now can't stand not having strict typing. I'm sure others have written about this, but these days I think good code is code which has a very small area of variability. E.g code which returns a value in a single place as a single type, has a very limited number of params (a…

> Whenever I see methods with 5+ params

I don't see why that's a problem. If a function implements an algorithm with several parameters (e.g. a formula with multiple variables), those values have to be passed somehow. Does it make a difference if they're in a configuration object or as distinct parameters?

Re: John Carmack on mutable variables

#392
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's moved toward the edges of your program. In a lot of functional languages, places that can perform these effects are marked explicitly.

For example, in Haskell, any function that can perform IO has "IO" in the return type, so the "printLine" equivalent is: "putStrLn :: String -> IO". (I'm simplifying a bit here). The result is that you know that a function like "getUserComments :: User -> [CommentId]" is only going to do what it says on the tin - it won't go fetch data from a database, print anything to a log, spawn new threads, etc.

It gives similar organizational/clarity benefits as something like "hexagonal architecture," or a capabilities system. By limiting the scope of what it's possible for a given unit of code to do, it's faster to understand the system and you can iterate more confidently with code you can trust.

Re: John Carmack on mutable variables

#393

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.

I guess I'm not that good a programmer, because I don't really understand why variables that can't be varied are useful, or why you'd use that. How do you write code that actually works?

The concept is actually pretty simple: instead of changing existing values, you create new values.

The classic example is a list or array. You don't add a value to an existing list. You create a new list which consists of the old list plus the new value. [1]

This is a subtle but important difference. It means any part of your program with a reference to the original list will not have it change unexpectedly. This eliminates a large class of subtle bugs you no longer have to worry about.

[1] Whether the new list has completely new copy of the existing data, or references it from the old list, is an important optimization detail, but either way the guarantee is the same. It's important to get these optimizations right to make the efficiency of the language practical, but while using the data structure you don't have to worry about those details.

Re: John Carmack on mutable variables

#394
post #12

How fast this got to the top, you would think John Carmack just invented nuclear fusion.

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.

Good old Gell-Mann Amnesia! It doesn't mean he's incompetent in his core area of expertise, though.

Re: John Carmack on mutable variables

#395

I 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?

> What is a "variable" if not something that varies?

If I define `function f(x) { ... }`, even if I don't reassign x within the function, the function can get called with different argument values. So from the function's perspective, x takes on different values across different calls/invocations/instances.

Re: John Carmack on mutable variables

#396
post #188

Earlier 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.

Or they got inspired by how this is done in OCaml, which was the host language for the earliest versions of Rust. Actually, this is a behaviour found in many FP languages. Regarding OCaml, there was even a experimental version of the REPL where one could access the different variables carrying the same name using an ad-hoc syntax.

Re: John Carmack on mutable variables

#397

> 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.

> indirection isn't free for readability

Yes, but also no. If its a mostly side-effect free function with a good name and well defined input/output types its basically free.

Re: John Carmack on mutable variables

#398

Earlier quoted context omitted.

Carmack had said he likes rust, he just isn't a language zealot. In rust though, the idiom would be to shadow intermediate variables often, which removes the debugger benefit.

> the idiom would be to shadow intermediate variables often There is no idiom about this. Do it if you like but clipply doesn't warn about any of it.

There isn't an enforced one, and the opinion is bifurcated, but I regularly find crates that clearly treat this as idiomatic practice.

Re: John Carmack on mutable variables

#399
post #385

Earlier quoted context omitted.

> 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.

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 assumes that's the same thing the result is confusing for programmers and so it usually ends up requiring a language level fix.

e.g. "In C# 5, the loop variable of a foreach will be logically inside the loop"

Re: John Carmack on mutable variables

#400

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.

I think the advantage is often oversold and people often miss how things actually exist on a continuum and just plainly opposing mutable and immutable is sidestepping a lot of complexity. For exemple, it's endlessly amusing to me to see all the efforts the Haskell community does to basically reinvent mutability in a way which is somehow palatable to their type system. Sometimes they even fail to even realise that it'…

If you read through the Big Red Book¹ or its counterpart for Kotlin², it's quite explicit about the goals with these techniques for managing effects, and goes over rewriting imperative code to manage state in a "pure" way.

I think the authors are quite aware of the relationship between these techniques and mutable state! I imagine it's similar for other canonical functional programming texts.

Besides the "pure" functional languages like Haskell, there are languages that are sort of immutability-first (and support sophisticated effects libraries), or at least have good immutable collections libraries in the stdlib, but are flexible about mutation as well, so you can pick your poison: Scala, Clojure, Rust, Nim (and probably lots of others).

All of these go further and are more comfortable than just throwing `const` or `.freeze` around in languages that weren't designed with this style in mind. If you haven't tried them, you should! They're really pleasant to work with.

----

1: https://www.manning.com/books/functional-programming-in-scal...

2: https://www.manning.com/books/functional-programming-in-kotl...

Post reply on HN