Live data from Hacker News

John Carmack on mutable variables

twitter.com

621–630 of 663 posts

Re: John Carmack on mutable variables

#622

Earlier quoted context omitted.

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

You can make everything read-only by default, and if you need non-read-only, you use "mutable".

You need two keywords. One for assignability and one for writability :

    const ptr;  // can't reassign, can't write-through (if r/o by default)
    const mut ptr;  // can write

Re: John Carmack on mutable variables

#623

Earlier quoted context omitted.

Well, the stuff I'm writing is in C, but in general it would make no sense for anything to attempt to add items to a fixed-sized buffer. If you have something so fundamentally broken as to attempt that, you'd probably want to look at mutexes. Why one earth would you have something attempt to expand a fixed-sized buffer while something else is working on it?

There’s a mismatch between your assumptions coming from C and GP’s assumptions coming from a language where arrays are not fixed-length. Having a garbage collector manage memory for you is pretty fundamental to immutable-first languages. Rich Hickey asked once in a talk, “who here misses working with mutable strings?” If you would answer “I do,” or if you haven’t worked much in languages where strings are always immu…

I must admit I do regard assembly language with some suspicion, because the assembler can make some quite surprising choices. Ultra-high-level languages like C are worse, though, because they can often end up doing things like allocating really wacky bits of memory for variables and then having to get up to all sorts of stunts to index into your array.

Re: John Carmack on mutable variables

#624
post #615

Earlier quoted context omitted.

> You don't have to use recursion You're using recursion. `runreq()` calls `sum_()` which calls `sum()` in `return l[0] + f(f, l[1:])`, where `f` is `sum()`

> You're using recursion. No, see GP. > `runreq()` calls `sum_()` which calls `sum()` in `return l[0] + f(f, l[1:])`, where `f` is `sum()` Also no, see GP.

I am too stupid to understand this. This:

    def sum_(f, l):
      if not l: return 0
      return l[0] + f(f, l[1:])

    def runreq(f, *args):
      return f(f, *args)

    print(995,runreq(sum_, range(1,995)))
    print(1000,runreq(sum_, range(1,1000)))
when run with python3.11 gives me this output:

    995 494515
    Traceback (most recent call last):
      File "/tmp/sum.py", line 9, in 
        print(1000,runreq(sum_, range(1,1000)))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/tmp/sum.py", line 6, in runreq
        return f(f, *args)
               ^^^^^^^^^^^
      File "/tmp/sum.py", line 3, in sum_
        return l[0] + f(f, l[1:])
                      ^^^^^^^^^^^
      File "/tmp/sum.py", line 3, in sum_
        return l[0] + f(f, l[1:])
                      ^^^^^^^^^^^
      File "/tmp/sum.py", line 3, in sum_
        return l[0] + f(f, l[1:])
                      ^^^^^^^^^^^
      [Previous line repeated 995 more times]
    RecursionError: maximum recursion depth exceeded in comparison
A RecursionError seems to indicate there must have been recursion, no?

Re: John Carmack on mutable variables

#625

Earlier quoted context omitted.

Made a similar experience with Scheme. I could tell people whatever I wanted, they wouldn't really realize how much cleaner and easier to test things could be, if we just used functions instead of mutating things around. And since I was the only one who had done projects in an FP language, and they only used non-FP languages like Java, Python, JavaScript and TypeScript before, they would continue to write things base…

I'm really afraid that the weak point of the argument is really Scheme having a Lisp syntax. One might say syntax is the most superficial thing about a language but as a matter of fact it's the mud pool in front of the property where everybody's wheels get stuck and they feel their only option is to go into reverse and maybe try another day, or never. The same happens with APL; sure it's a genius who invented it and…

Doesn't even have to be true copies. Structural sharing is a thing, that enables many or most functional data structures and avoids excessive memory usage. I agree with your point, and it would put JS higher in my liked languages list.

Re: John Carmack on mutable variables

#626
post #487

Earlier quoted context omitted.

I would never do `response = response.json()`. I use it when it's effectively the same type, but with further processing which may be optional.

Depends on how clear it is. I usually write code to help local debug-ability (which seems rare). For example, this allows one to trivially set a conditional breakpoint and look into the full response: response = get_response() response = response.json() The fact that the first response is immediately overwritten proves to the reader it's not important/never used, so they can forget about it, where a temp variable wou…

    get_response().json() 
is ideal, and I'm assuming yoiu're writing an HTTP wrapper since decoding JSON is a sensible default.

If you need to add an intermediary variable, name it as clearly as possible:

    raw_response = get_response()
    response = raw_response.json()

Re: John Carmack on mutable variables

#627
post #603

Earlier quoted context omitted.

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.

There is no exception for ANY data structure that includes references to other data structures or primitives. Not only can you add or remove elements from an array, you can change them in place. A const variable that refers to an array is a const variable. The array is still mutable. That's not an exception, its also how a plain-old JavaScript object works: You can add and remove properties at will. You can change it…

I know, and I do agree it's consistent, but then it doesn't make any sense to me as a keyword in a language where non-primitives are always by-reference.

You can't mutate the reference, but you _can_ copy the values from one array into the data under an immutable reference, so const doesn't prevent basically any of the things you'd want to prevent.

The distinction makes way more sense to me in languages that let you pass by value. Passing a const array says don't change the data, passing a const reference says change the data but keep the reference the same.

Re: John Carmack on mutable variables

#628

Earlier quoted context omitted.

Can you clarify?

Modern CPUs do out-of-order execution, which means they need to identify and resolve register sharing dependencies between instructions. This turns the notional linear model of random-access registers into a DAG in practice, where different instructions that might be in flight at once actually read from or write to different "versions" of a named register. Additionally, pretty much every modern CPU uses a register re…

Realistically, the compiler is building a DAG called SSA; and then the CPU builds a DAG to do out of order execution, so at a fine grain -- the basic block -- it seems to me that the immutable way of thinking about things is actually closer to the hardware.

Re: John Carmack on mutable variables

#629

Earlier quoted context omitted.

It ends up being quite the opposite - many, many bugs come from unexpected side effects of mutation. You pass that array to a function and it turns out 10 layers deeper in the call stack, in code written by somebody else, some function decided to mutate the array. Immutability gives you solid contracts. A function takes X as input and returns Y as output. This is predictable, testable, and thread safe by default. If…

Okay, so this sounds like it's a method of programming that is entirely incompatible with anything I work on. What sort of thing would it be useful for? The kind of things I do tend to have maybe several hundred thousand floating point values that exist for maybe a couple of hundred thousandths of a second, get processed, get dealt with, and then are immediately overwritten with the next batch. I can't think of any r…

It is useful for the vast majority of business processing. And, if John Carmack is to be believed, video game development.

Carmamack's post explains it - if you make a series of immutable "variables" instead of reassigning one, it is much easier to debug. This is a microcosm of time travel debugging; it lets you look at the state of those variables several steps back.

In don't know anything about your specific field but I am confident that getting to the point where you deeply understand this perspective will improve your programming, even if you don't always use it.

Re: John Carmack on mutable variables

#630

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.

Made a similar experience with Scheme. I could tell people whatever I wanted, they wouldn't really realize how much cleaner and easier to test things could be, if we just used functions instead of mutating things around. And since I was the only one who had done projects in an FP language, and they only used non-FP languages like Java, Python, JavaScript and TypeScript before, they would continue to write things base…

Python is like a mutation wet-dream. The language is so broken in modern times.
Post reply on HN