28 ms·
John Carmack on mutable variables
- munchler 1y ago> Making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword. It's funny how functional programming is slowly becoming the best practice for modern code (pure functions, no side-effects), yet functional programming languages are still considered fringe tech for some reason. If you want a language where const is the default and mutable is a keyword, try F# for starters. I switched and never looked back.
- Terr_ 1y agoWhile it's amusing, I think it's sensible: One of the main tasks in most businessy programming is to take what a human wants, translate it to code, reverse-translate it back to human understanding later, modify it, and translate it again to slightly different code. This creates friction between casual stakeholder models of a mutable world, versus the the assumptions an immutable/mostly-pure language imposes. When the customer describes what they need, that might be pretty close to a plain loop with a variable that increments sometimes and which can terminate early. In contrast, it maps less-cleanly to a pure-functional world, if I'm lucky there will at least be a reduce-while utility function, so I don't need to make all my own recursive plumbing. So immutability and pure-functions are like locking down a design or optimizing--it's not great if you're doing it prematurely. I think that's why starting with mutable stuff and then selectively immutable'ing things is popular. Come to think of it, something similar can be said about weak/strong typing. However the impact of having too-strong typing seems easier to handle with refactoring tools, versus the problem of being too-functional.
- Freedom2 1y agoDecades of blog posts, articles and books about "best practices" led to a suspicion of anything that wasn't bog standard OOP. This site in particular has also contributed to that, especially in the early 2000s, where you can easily find comments disparaging FP.
- jeffreygoesto 1y agoI think there's a deeper mechanism in these discussions. They all tend to go "If you do <insert_arbitrary_method_here> fully correct[0], it solves all of your problems." But [0] is never possible.
- josephg 1y ago> If you want a language where const is the default and mutable is a keyword, try F# for starters. I switched and never looked back. Rust is also like this (let x = 5; / let mut x = 5;). Or you can also use javascript, typescript and zig like this. Just default to declaring variables with const instead of let / var. Or swift, which has let (const) vs var (mutable). FP got there first, but you don't need to use F# to have variables default to constants. Just use almost any language newer than C++.
- bathtub365 1y agoAre there languages that automatically extend this to things like data structure members? One of the things I like about the C++ const keyword is that if you declare an instance of a struct/class as const it extends that to its members. If the instance isn’t const, you can still mutate them (as long as they aren’t declared const within the structure itself)
- orlp 1y agoRust works this way, yes. There are escape hatches though, which allow interior mutability.
- snalty 1y agoYeah, here's an example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=1c4609c5f0bca55613490f2a80d0c026 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- School-Cotton 1y agoIf I understand what you’re asking correctly, rust is also like this. If you have a non-mut value of (or non-mut reference to) an object, you only get non-mut access to its members.
- Fire-Dragon-DoL 1y agoIn typescript and js you get immutable references, but the data is mutable. Definitely not the same thing
- ehsankia 1y agofunctional programming has a lot of wonderful concepts, which are very interesting in theory, but in practice, the strictness of it edges on annoying and greatly hurts velocity. Python has a lot of functional-like patterns and constructs, but it's not a pure functional language. Similarly, Python these days allow you to adds as much type information as you want which can provide you a ton of static checks, but it's not forced you like other typed languages. If some random private function is too messy to annotate and not worth it, you can just skip it. I like the flexibility, since it leads to velocity and also just straight up more enjoyable.
- tasn 1y agoFunctional programming languages (almost always?) come with the baggage of foreign looking syntax. Additionally, imperative is easier in some situations, so having that escape hatch is great. I think that's why we're seeing a lot of what you're describing. E.g. with Rust you end up writing mostly functional code with a bit of imperative mixed in. Additional, most software is not pure (human input, disk, network, etc), so a pure first approach ends up being weird for many people. At least based on my experience.
- deleted 1y ago[deleted]
- rapind 1y ago> come with the baggage of foreign looking syntax Maybe they're right about the syntax too though? :)
- kragen 1y agoWhich one, Erlang, Lisp, or ML?
- nickpeterson 1y agoHonestly I find ML derived languages the most pleasant to look at.
- kragen 1y agoI prefer Piet, but it has some real drawbacks when it comes to readability: https://www.dangermouse.net/esoteric/piet/samples.html https://www.dangermouse.net/esoteric/piet/samples.html
- tmtvl 1y agoLisp syntax is objectively superior because you can write a predicate like thus: (< lower-bound x-coordinate upper-bound) Instead of having to do the awful ampersand dance.
- the__alchemist 1y agoI think it's because (I'm looking at Haskell in particular) there are a lot of great ideas implemented in them, but the purity makes writing practical or performant time-domain programs high friction. But you don't need both purity and the various tools they provide. You can use the tools without the pure-functions model. In particular: My brain, my computing hardware, and my problems I solve with computers all feel like a better match for time-domain-focused programming.
- cogman10 1y agoMy problem with functional languages is there never seems to be any easy way to start using them. Haskell is a great example here. Last time I tried to learn it, going on the IRC channel or looking up books it was nothing but a flood of "Oh, don't do that, that's not a good way to do things." It seemed like nothing was really settled and everything was just a little broken. I mean, Haskell has like what, 2, 3, 4? Major build systems and package repositories? It's a quagmire. Lisp is also a huge train wreck that way. One does not simply "learn lisp" There's like 20+ different lisp like languages. The one other thing I'd say is a problem that, especially for typed functional languages, they simply have too many capabilities and features which makes it hard to understand the whole language or how to fit it together. That isn't helped by the fact that some programmers love programming the type system rather than the language itself. Like, cool, my `SuperType` type alias can augment an integer or a record and knows how to add the string `two` to `one` to produce `three` but it's also an impossible to grok program crammed into 800 characters on one line.
- v9v 1y ago> Lisp is also a huge train wreck that way. [...] There's like 20+ different lisp like languages. Lisp is not a language, but a descriptor for a family of languages. Most Lisps are not functional in the modern sense either. Similarly, there are functional C-like languages, but not all C-likes are functional, and "learn c-likes" is vague the same way "learn lisp" is.
- School-Cotton 1y ago
- jandrewrogers 1y agoPure functional works great until it doesn't. For a lot of systems-y and performance-oriented code you need the escape hatches or you'll be in for a lot of pain and annoyance. As a practical observation, I think it was easier to close this gap by adding substantial functional capabilities to imperative languages than the other way around. Historically, functional language communities were much more precious about the purity of their functional-ness than imperative languages were about their imperative-ness.
- jancsika 1y agoHehe, purity is one helluva drug! For some reason, this makes me think of SVG's foreignObject tag that gives a well-defined way to add elements into an SVG document from an arbitrary XML namespace. Display a customer invoice in there, or maybe a Wayland protocol. The sky's the limit! On the other hand, HTML had so many loose SVG tags scattered around the web that browsers made a special case in the parser to cover them without needing a namespace. And we all know how that played out. Posted from an xhtml foreignObject on my SVGphone
- Fire-Dragon-DoL 1y agoUnfortunately unless there's an explicit way to state what has side effects like a mut keyword, a lot of fp programming advantages lose value because most devs default to mutable stuff, so the fp benefits don't compound
- black_knight 1y agoI think this works quite well with IO in Haskell. Most of my code is pure, but the parts which are really not, say OpenGL code, is all marked as such with IO in their type signatures. Also, the STM monad is the most carefree way of dealing with concurrency I have found.
- christophilus 1y agoF# isn’t purely functional, though, and strikes a nice balance. I just don’t really like the .NET ecosystem, being 100% Linux these days, as it always seems slightly off to me somehow.
- keeda 1y agoWe are still living with the hangover of C, which was designed for the resource-starved machines of eons ago, and whose style later languages felt they had to copy to get any kind of adoption. (And as you point out, that is how things turned out.) My bet is functional programming will become more and more prevalent as people figure out how to get AI-assisted coding to work reliably. For the very reasons you stated, functional principles make the code modular and easy to reason about, which works very well for LLMs. However, precisely because functional programming languages are less popular and hence under-represented in the training data, AI might not work well with them and they will probably continue to remain fringe.
- johnisgood 1y agoJust use OCaml in which you can mix imperative, functional, and OOP. I use all of them in a single codebase, whichever, wherever appropriate.
- charcircuit 1y agoConst by default is not functional programming.
- School-Cotton 1y agoIndeed, but it's one of the (many) good ideas from functional programming that have filtered into more mainstream languages.
- brrrrrm 1y agoone thing I've learned in my career is that escape hatches are one of the most important things in tools made for building other stuff. dropping down into the familiar or the simple or the dumb is so innately necessary in the building process. many things meant to be "pure" tend to also be restrictive in that regard.
- runevault 1y agoFunctional languages are not necessarily pure though. Actually outside Haskell don't most functional first languages include escape hatches? F# is the one I have the most experience with and it certainly does.
- gf000 1y agoFor what it's worth, Haskell has plenty of escape hatches itself as well.
- johncolanduoni 1y agoI’ve done a significant amount of functional programming (including F#) and still reach for it sometimes, but I don’t think it provides substantial advantages for most use-cases. Local mutability is often clearer and more maintainable. Also, category theorists think how excited people get about using the word monad but then most don’t learn any other similar patterns (except maybe functors) is super cringe. And I agree.
- otikik 1y agoDon't get too comfortable, history likes oscillation. If it becomes the norm then someone will have a "actually writing imperatively makes things go super fast".
- LogicHound 1y ago> It's funny how functional programming is slowly becoming the best practice for modern code (pure functions, no side-effects), I once mentioned both these concepts to a room of C# developers. Two of them were senior to me and it was a blank expression from pretty much everyone. > yet functional programming languages are still considered fringe tech for some reason. You can use the same concepts in non-functional programming languages without having to buy into all the other gumpf around functional programming languages. Also other programming languages have imported functional concepts either into the language itself or into the standard libraries. Past that. It is very rare to be able to get a job using them. The number of F# jobs I've seen advertised over the last decade, I could count on one hand.
- gf000 1y agoIt's because you want a tasteful mix of both. I believe Scala was pretty ahead here by building the language around local mutability with a general preference for immutable APIs, and I think this same philosophy shows up pretty strongly in Rust, aided by the borrow checker that sort of makes this locality compiler-enforced (also, interior mutability)
- dionian 1y agoWorth nothing that idiomatic scala uses constants by default, variables are discouraged and frankly rare.
- physicles 1y agoAgreed, and Carmack as always was ahead of the curve here. In case anyone here hasn’t seen it, here’s his famous essay, Functional Programming in C++: http://sevangelatos.com/john-carmack-on/ http://sevangelatos.com/john-carmack-on/ He addresses your point: > I do believe that there is real value in pursuing functional programming, but it would be irresponsible to exhort everyone to abandon their C++ compilers and start coding in Lisp, Haskell, or, to be blunt, any other fringe language. To the eternal chagrin of language designers, there are plenty of externalities that can overwhelm the benefits of a language, and game development has more than most fields.
- skeezyjefferson 1y ago> If you want a language where const is the default and mutable is a keyword whats the difference between const and mutable?
- Thorrez 1y ago"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. As opposed to how C++ works today, where there is no "mutable" keyword, variables are mutable by default, and we use a "const" keyword to mark them constant.
- alcover 1y ago> you don't need both a "const" keyword and a "mutable" keyword What if the lang has pointers? How express read-only?
- skeezyjefferson 1y agocompiler flags with line and column number seems like the easiest way
- Thorrez 1y agoYou can make everything read-only by default, and if you need non-read-only, you use "mutable".
- alcover 1y agoYou 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
- bmitc 1y agoI get your sentiment, but I side on it's infuriating that it's taken this long. Lol. F# is wonderful. It is the best general purpose language, in my opinion. I looked for F# jobs for years and never landed one and gave up. I write Rust now which has opened up the embedded world, and it's nice that Rust paid attention to F# and OCaml.
- danenania 1y agoIt’s because FP, great as it is, is most beneficial when 80/20’d. Trying to do it 100% pure makes everything take significantly longer to build and also makes performance difficult when you need it. It’s also hard to hire/onboard people who can grok it.
- themafia 1y agoI want a language that helps me write software. I do not need a language that's hellbent on expressing a particular ideology.
- hyperhello 1y ago> I wish it was the default, and mutable was a keyword. I wish the IDE would simply provide a small clue, visible but graphically unobtrusive, that it was mutated. In fact, I end up wishing this about almost every language feature that passes my mind. For example, I don't need to choose whether I can or can't append to a list; just make it unappendable if you can prove I don't append. I don't care if it's a map, list, set, listOf, array, vector, arrayOf, Array.of(), etc unless it's going to get in my way because I have ten CPU cores and I'll optimize the loop when I need to.
- bee_rider 1y agoI don’t have any useful ideas here but if you make a linter for this sort of thing, I suggest calling it “mutalator.”
- estimator7292 1y agoYour IDE probably supports this as an explicit action. JetBrains has a feature that can find all reads and writes to a variable
- Denvercoder9 1y agoIt also has the ability to style mutated variables differently.
- greenicon 1y agoYes, depending on your highlighting scheme. Not every highlighting scheme shows this by default, unfortunately. To me, this seems initially like some very minor thing, but I find this very helpful working with non-trivial code. For larger methods you can directly discern whether a not-as-immutable-declared variable behaves immutable nonetheless.
- nielsbot 1y agoI 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.
- gwbas1c 1y agoYears ago I did a project where we followed a lot of strict immutability for thread safety reasons. (Immutable objects can be read safely from multiple threads.) It made the code easier to read because it was easier to track down what could change and what couldn't. I'm now a huge fan of the concept.
- ronnier 1y agoImmutability was gaining huge traction with Java... then large parts of the industry switched to golang and we hardly make anything immutable.
- antonvs 1y agoGo is the new PHP.
- LogicHound 1y agoMuch like PHP, you can actually get stuff done unlike a lot of other programming languages.
- antonvs 1y agoOh? Which “lot of” other programming languages can’t you “actually get stuff done” in? Are you sure the problem lies with the programming language?
- LogicHound 1y agoI find there are some environments where you have a positive feedback loop while working in them. PHP is one of them, Go is another at least for me. I find many of languages I am constantly fighting with dependency managers, upgrades and all sorts of other things.
- danenania 1y agoIt’s an exaggeration perhaps but I get the sentiment. FP is elegant and beautiful and everything, but it can lead you to spend all day puzzling out the right abstractions for some data transformation that takes 5 minutes with a dumb for loop in Go.
- ottah 1y agoI would kill for an immutable error type
- 1y ago
- bitwize 1y ago[flagged]
- embedding-shape 1y agoEh, Rust is kind of "immutable by default" but not really enforcing that + "constants" in the way I think Carmack advocates for. Other languages does better in that regard. Examples: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=8b864cb97d9de1f91ff661188d1f3486 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- jasonthorsness 1y agoYeah the encouragement of shadowing is a little weird (learning Rust coming from Go where it is sort of discouraged)
- tialaramex 1y agoDiscouraging shadowing in languages with unclear lifetimes makes plenty of sense. Clippy offers lints for (three?) distinct ways of shadowing because in most cases it turns out people who don't like shadowing only had problems with typically one specific kind of shadowing (e.g. same type unrelated shadowing, or different type same value shadowing) and since that varies why not offer to diagnose the specific problem this programmer doesn't like. To be concrete some people are worried about things like: let sparrows = get_a_list_of_sparrows(); // .... let sparrows = sparrows.len() + EXTRA_SPARROWS; Whereas for some people that seems fine but they're worried about: let sparrows = find_wild_sparrows(); // .... let sparrows = find_farmed_sparrows(); These are both shadowing the name sparrows, and Rust is fine with either but Clippy can provide different lints so that you can ban one of them while using the other freely in your codebase.
- veltas 1y agoI hope this experimental language becomes usable eventually, all I hear is how you have to continuously rewrite to work around the borrow checker, accept worse performance to make everything message based, or just ignore the type system and write VB6-style code where everything is indexes in arrays to obfuscate ownership and paste 'unsafe' when it moans.
- lopatin 1y agoHow fast this got to the top, you would think John Carmack just invented nuclear fusion.
- viraptor 1y agoSometimes it's nice to be reminded of some basic good ideas. Even if you already know. Also https://xkcd.com/1053/ https://xkcd.com/1053/
- AndrewOMartin 1y agoI have no doubt that he could, not only invent nuclear fusion, but get it running on a Pentium 90.
- villgax 1y agoJust like AGI he was supposedly brought on board for but…..checks notes. Nothing.
- jpgvm 1y agoDude isn't a god. That said it's worth listening when he chimes in about C/C++ or optimisation as he has earned his respect when it comes to these fields. Will he crack AGI? Probably not. He didn't crack rockets either. Doesn't make him any less awesome, just makes him human.
- TheAceOfHearts 1y agoThere are certain figures who are very experienced and knowledgeable in certain domains, so when they speak up about a topic it's usually worth listening to them. That doesn't mean they're always going to be correct, and they shouldn't be worshiped as superhuman entities, but it's almost always a bad idea to completely ignore them.
- AnotherGoodName 1y ago>Just like AGI he was supposedly brought on board for but…..checks notes. Nothing. His AGI work was entirely his own? As in he literally stepped down from a high level corporate role where he was responsible for Oculus (3D games/applications) to do this in his own time. Similar to his work on Armadillo Aerospace.
- noduerme 1y agoWhy loops specifically? Why not conditionals? A lot of code needs to assemble a result set based on if/then or switch statements. Maybe you could add those in each step of a chain of inline functions, but what if you need to skip some of that logic in certain cases? It's often much more readable to start off with a null result and put your (relatively functional) code inside if/then blocks to clearly show different logic for different cases.
- turtletontine 1y agoThere’s no mutating happening here, for example: if cond: X = “yes” else: X = “no” X is only ever assigned once, it’s actually still purely functional. And in Rust or Lisp or other expression languages, you can do stuff like this: let X = if cond { “yes” } else { “no” }; That’s a lot nicer than a trinary operator!
- nielsbot 1y agoSwift does let you declare an immutable variable without assigning a value to it immediately. As long as you assign a value to that variable once and only once on every code path before the variable is read: let x: Int if cond { x = 1 } else { x = 2 } // read x here
- lock1 1y agoIMO, both ternary operator form & Rust/Haskell/Zig syntax works pretty well. Both if expression syntax can be easily composed and read left-to-right, unlike Python's `<true-branch> if <cond> else <false-branch>`.
- fallat 1y ago[flagged]
- EdwardKrayer 1y agoJohn Carmack is my spirit animal. I like reading his thoughts because they give me strange insight into the way he thinks "sideways" sometimes - even if I disagree with them, I always enjoy an outside perspective.
- embedding-shape 1y agoI don't think this is the first time Carmack been talking about functional programming and against mutability, here is some of his thoughts from 2014: http://number-none.com/blow/john_carmack_on_inlined_code.html http://number-none.com/blow/john_carmack_on_inlined_code.htm... Just because someone is expressing a thought after you or others have already thought about it, doesn't mean they're trying to push it as something new and exciting, it's just another perspective.
- deleted 1y ago[deleted]
- Pxtl 1y ago[flagged]
- swiftcoder 1y ago[flagged]
- shmerl 1y agoProposing making immutable by default in C or C++ doesn't make sense due to backwards compatibility reasons. New languages like Rust have easier time making better choices with immutable by default.
- loeg 1y agocpp2/cfront could plausibly do this, right? Except he doesn't want to: https://github.com/hsutter/cppfront/wiki/Design-note%3A-const-objects-by-default https://github.com/hsutter/cppfront/wiki/Design-note%3A-cons...
- shmerl 1y agoWell, if they are willing to break backwards compatibility, a lot of things can be improved, including this.
- NathanaelRea 1y agoThey could just add a "use immutable;" directive that you place at the top of your file.
- Pxtl 1y agoC# does this with the null hole. I wish more languages would take a versioning approach to defaults at the file-level.
- JonChesterfield 1y agoCould be a compiler flag. -const-by-default. Would probably mean you need to scatter mutable across the codebase to get it to compile, but I expect some people would like to have every local annotated as const or mutable.
- _flux 1y agoMaybe the new C++ profiles that are supposedly going to make C++ a safe language could do it.
- GPerson 1y agoKinda curious on what jblow would say about this.
- kragen 1y agoblojo? Ask him and report back.
- GPerson 1y ago[flagged]
- kragen 1y agoNo, he's a competent programmer.
- swiftcoder 1y ago> > Lol what a loser > No, he's a competent programmer. I don't think these are mutually exclusive
- loeg 1y agoYeah, I also wish it was the default. But it's a little too verbose to just sprinkle on every variable in C++. Alas. Rust gets this right, but I'm stuck with C++ at work.
- veltas 1y ago100%, life's too short. Ultra-pedantic const-correctness (vs tasteful const-correctness on e.g. pass-by-reference arguments or static/big objects) catches nearly no bugs in practice and significantly increases the visual noise of your code. If you have luxury of designing a new language or using one with default mutability then do so, but don't turn C coding styles into C++-envy, or C++ coding styles into Rust-envy.
- maleldil 1y ago> But it's a little too verbose to just sprinkle on every variable in C++ It's worth it, though. Every variable that isn't mutated should be const. Every parameter not mutated should be const, and so should every method that doesn't mutate any fields. The mutable keyword should be banned.
- loeg 1y agoConst annotations are worth it for methods and APIs, but not most local variables.
- maleldil 1y agoI'd argue that it is. Use const by default, so it's obvious to the reader if something is mutated or not. It's easier to read code when you don't have to worry about how values may change over time. This is why Rust made the right choice.
- loeg 1y agoYeah, I think it's a great default, and Rust got that right. It's just too noisy in C++ as the non-default.
- DashAnimal 1y agohttps://nitter.net/id_aa_carmack/status/1983593511703474196 https://nitter.net/id_aa_carmack/status/1983593511703474196
- DoctorOW 1y agoI wish this was one of HN's auto formatting rules, automatically replace the link with one of these frontends
- DashAnimal 1y agoTbh I think it's ok - as much as I also want to avoid Twitter I do encourage original sourcing, especially since these nitter services can also have downtime fairly often. As long as someone jumps in and shares a link
- LeoPanthera 1y ago[flagged]
- whimsicalism 1y agoat least in ML, no other social media forum has comparably technical depth - would love for people to point me to others
- loeg 1y agoNo Twitter clone has supplanted the original. Bluesky is a joke. Federation doesn't work. Threads isn't close. Musk's ownership isn't such a negative that it outweighs everything else.
- Pxtl 1y ago> Musk's ownership isn't such a negative that it outweighs everything else. If it was just his ownership that would be one thing but his aggressive support for turning the site into /pol/ has ruined it.
- bigstrat2003 1y agoThat is not really a reasonable take. Judging people for what website they use is extremely shallow.
- Pxtl 1y ago[flagged]
- tomhow 1y agoPlease don't post off-topic flamebait on HN. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- Pxtl 1y agoHow is it offtopic on a Twitter post by John Carmack to remark on the fact that John Carmack posts to Twitter?
- kins 1y ago[flagged]
- deleted 1y ago[deleted]
- ottah 1y agoI mean, he's very accomplished, with most of it being well known by the average user.
- JanneVee 1y agoBecause he showed the action fps was possible on limited hardware. Also he has had some good ideas in the software- design and architecture of Wolfenstein, Doom and Quake, that where apparent when he open sourced their engines.
- stevage 1y agoIn JavaScript, I really like const and have adopted this approach. There are some annoying situations where it doesn't work though, to do with scoping. Particularly: - if (x) { const y = true } else { const y = false } // y doesn't exist after the block - try { const x = foo } catch (e) { } // x doesn't exist after the try block
- NathanaelRea 1y agoYou could do an absolutely disgusting IIFE if you need the curly brace spice in your life, instead of a typical JS ternary. const y = (() => { if (x) { return true; } else { return false; })();
- cookiengineer 1y agoTechnically you could just use an assignment ternary expression for this: const y = (x === true) ? true : false; I used this kind of style for argument initialization when I was writing JS code, right at the top of my function bodies, due to ES not being able to specify real nullable default values. (and I'm setting apart why I think undefined as a value is pointless legacy). Composite.prototype.SetPosition(x, y, z) { x = (isNumber(x) && x >= 0 && x <= 1337) ? x : null; y = (isNumber(y) && y >= 0 && y <= 1337) ? y : null; z = isNumber(z) ? z : null; if x !== null && y !== null && z !== null { // use clamped values } }
- NathanaelRea 1y agoI typically only use ternaries for single operations and extract to a function if it's too big. Although they are quite fun in JSX. For your code i'd probably do: function SetPosition(x, y, z) { if (!(isNumber(x) && isNumber(y) && isNumber(z))) { // Default vals return; } x = clamp(x, 0, 1337); y = clamp(y, 0, 1337); z = z; }
- cookiengineer 1y ago
- askmrsinh 1y agoConstants are useful for reasoning about code, but anyone who focuses only on making everything an immutable is missing the point. The main goal should be achieving referential transparancy. It can be perfectly fine to use mutable variables within a block, like a function when absolutely needed - for example, in JavaScript's try catch and switch statements that need to set a variable for later use. As long as these assignments are local to that block, the larger code remains side-effect free and still easy to reason about, refactor and mantain. https://rockthejvm.com/articles/what-is-referential-transparency-and-why-should-you-care https://rockthejvm.com/articles/what-is-referential-transpar...
- swiftcoder 1y agoRediscovering one of the many great decisions that Erlang made
- DarkNova6 1y agoTo be fair, Carmack has advocated for immutability and other good practices at least since the 2000s.
- gorgoiler 1y agoI still have a habit of naming variables foo0, foo1, foo2, from a time working in Erlang many years ago.
- slifin 1y agoYeah I wish variables were immutable by default and everything was an expression Oh well continues day job as a Clojure programmer that is actively threatened by an obnoxious python take over
- sunrunner 1y agoAs a Python programmer at day job, that is Clojure-curious and sadly only gets to use it for personal projects, and is currently threatened by an obnoxious TypeScript take over, I feel this.
- dude250711 1y agoRemoving barriers to sloppy code is a language feature. That is why vibe coding, JavaScript and Python are so attractive.
- Xelbair 1y agoRemoving barriers to civil engineering building design is a feature. Who needs to calculate load bearing supports, walls, and floors when you can just vibe oversize it by 50%.
- huflungdung 1y agoWell if it does the job. So what?
- hn_throw2025 1y agoIn the context of the original discussion, TypeScript (and ES6) has const and let.
- dragonwriter 1y agoNeither let nor even const are immutable (const prevents reassignment but not mutation if the value is of a mutable type like object or array).
- mr_mitm 1y agoIs there a ruff rule for this?
- maleldil 1y agoThere are [1] and [2] for function arguments and loop variables, respectively, but nothing for the general case. Note that a type checker will complain if you re-assign with a different type. Pylint also has [3] for redefining variables from an outer scope, but Ruff doesn't implement that yet. [1] https://docs.astral.sh/ruff/rules/redefined-argument-from-local/ https://docs.astral.sh/ruff/rules/redefined-argument-from-lo... [2] https://docs.astral.sh/ruff/rules/redefined-loop-name/ https://docs.astral.sh/ruff/rules/redefined-loop-name/ [3] https://pylint.pycqa.org/en/latest/user_guide/messages/warning/redefined-outer-name.html https://pylint.pycqa.org/en/latest/user_guide/messages/warni...
- gethly 1y agoVariable is by definition mutable. Constant is by definition immutable. Why can't people get it through their heads in 2025? (I'm looking at you, Rust)
- mirpa 1y agoThat depends on your definition. Programming languages often deviate from mathematics when it comes to definition of variables, functions etc. That is by choice, Haskell tried to be as close to mathematical definition as possible.
- latexr 1y agoYou don’t need to make a mathematical argument, “variable” and “constant” have clear meanings in colloquial use which match the definitions of your parent comment.
- devnullbrain 1y agoSo does 'parent'.
- m000 1y agoDepends on how you look at it. A true constant won't change between runs of the code. I.e. it is essentially a symbolic name for a literal. A constant variable OTOH, varies in different executions of the code. So, its invariance is linked to an execution context.
- gf000 1y agoWhat about the result of a computation within a giant algorithm that I decided to name, and don't plan on rewriting it anymore? That's surely not a constant like PI, is it?
- witx 1y agoA constant is a variable that _always_ has the same value on all lifecycles, e.g. const int a{10}; An immutable variable well ... does not void some_function(const int a); Can you tell me what is the value of `a` on both cases?
- halo 1y agoOn a similar note, I’ve always liked the idea of being able to mark functions as pure (for some reasonable definition of pure). The principle of reducing state changes and side-effects feels a good one.
- jlmcgraw 1y agoFor what it's worth, I was experimenting with this idea for Python (in an almost completely vibe-coded fashion) here: https://github.com/jlmcgraw/pure-function-decorators https://github.com/jlmcgraw/pure-function-decorators Whether it's of any actual utility is debatable
- omnicognate 1y ago> In C/C++, making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword. I know it's irrelevant to his point, and it's not true of C, and it doesn't have the meaning he wants, but the pedant in me is screaming and I'm surprised it hasn't been said in the comments: In C++ mutable is a keyword.
- tsimionescu 1y agoIt's also really funny, since C++ is like a malicious genie with this. The author wished for mutable to be a keyword, and C++ made the wish come true: it's a keyword that removes some of the guarantees of const!
- y0ned4 1y agoWhen I started programming in Haskell, where all variables are immutable, I felt like I was in a straitjacket Then, suddenly, the enlightenment
- kypro 1y agoI'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 (also of single type), and has no mutable variables. If you can break code into chunks of highly predictable logic like this it's so so much easier to reason about your code and prevent bugs. Whenever I see methods with 5+ params and several return statements I can almost guarantee there will be subtle bugs.
- kalaksi 1y agoI initially started programming with C++, then did a bunch of scripting languages and then Rust and C#. I feel like there are pros and cons to strictness. I don't like all scripting languages, but Python, for example, has a simple syntax and is easy and fast to learn and write. I think it also has a good standard library. Some things can be simpler because of the missing guardrails. But that's also the weakness and I wouldn't use it for large and complex software. You have to be more mindful to keep a good style because of the freedoms. C++ and Rust are at the opposite end. Verbose to write and more difficult syntax. More stuff to learn. But in the end, that's the cost for better guarantees on correctness. But again, they don't fit all use cases as well as scripting languages. I've experienced the good and the bad in both kind of languages. There are tradeoffs (what a surprise) and it's probably also subjective what kind of language one prefers. I think my _current_ favorites are Rust, C# and Python.
- maleldil 1y ago> 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?
- jdthedisciple 1y agoThis is almost standard in modern languages: For example in Dart you make everything `final` by default.
- croes 1y agoA real default doesn’t need a keyword.
- EastLondonCoder 1y agoAfter 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.
- oldpersonintx2 1y ago[dead]
- deleted 1y ago[deleted]
- m_rpn 1y agosalutes from a WestLondonCoder
- emil0r 1y agoThe 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. At no point do you ever need to know about the state of the entire program, you just need to know the data and the function. I don't need the entire program up and running in order to test or debug this function. I just need the data that was sent in, which CANNOT be changed by any other part of the program.
- DrScientist 1y agoSure 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?
- Havoc 1y agoGood point. Had never occurred to me that keeping steps help debug. Obvious in hindsight
- Sammi 1y agoFor the same reason almost all my functions end with this: const result = ... ; return result; I know debuggers can show return values, but that doesn't help when you're just doing a quick console log inspection.
- ardit33 1y agoMeh... I agree where he comes from (when working on large projects, muttability can introduce bugs), but many languages have solved it, similar by having two types, 'let' and 'var', or 'const' in front of a variable. But, there is practicality in the ability of being able to change a var, and not having to create a new object every time you change one of its members. It models real nature/physics better. It looks like He is asking that 'const' be the default, and 'var' should be explicit, which makes sense.
- or_am_i 1y agoIn JetBrains editors it's possible to highlight mutable variables, at least in the languages where the distinction exists. My go to setting in Kotlin is to underscore all `var`'s, for two reasons: - this makes them really stand out, much easier to track the mutation visually, - the underscore effect is intrusive just-enough to nudge you to think twice when adding a new `var`. Nothing like diving into a >3k lines PR peppered with underscores.
- Razengan 1y agoIf designing your hypothetical ideal language, what are some intuitive/cute keywords you would choose for mutables/immutables? `let` is so 2020. `const` is too long. `static` makes me fall asleep at the keyboard. `con` sounds bad. How about `law`? law pi = 3.142 (heh typing on Mac autocompleted this) law c = 29979245 law law = "Dredd" or `set` or `once` or `make`?
- tomaytotomato 1y agoThere was a proposal in Java a few years back to introduce "val". I think it never gained traction but it would have been nice to have this in Java val firstName = "Bob"; val lastName = "Tables"; var fullName = ""; fullName = firstName + " " + lastName;
- ratmeadow 1y agoWouldn't this "val" be the same as "final"? Also related, it annoys me that Java has final but otherwise poor/leaky support for immutability. You can mark something final but most Java code (and a lot of the standard library) uses mutable objects so the final does basically nothing... C++ "const" desparately needs to spread to other languages.
- Razengan 1y agoThat's an aesthetically awkward and also bug-prone syntax: So just a difference of 1 single letter (that looks similar) to mean the completely opposite thing?? Nah you don't want that, and I don't either.
- nvlled 1y agoKotlin uses var/val too[0] which is what Java is trying to copy. I have never written any kotlin code before, so I don't know if this would be a problem in practice. On the plus side, var and val both have the same length, so the variable declaractions are properly aligned. The names are also intuitive as far as I can tell.In theory, I'd probably be okay with it. [0] https://kotlinlang.org/docs/basic-syntax.html#variables https://kotlinlang.org/docs/basic-syntax.html#variables
- considerdevs 1y agoThis is the kind of wisdom that comes after hours of debugging and discovering the bug was your own variable reuse. Wouldn't this be an easy task for SCA tool e.g. Pylint? It has atleast warning against variable redefinition: https://pylint.pycqa.org/en/latest/user_guide/messages/refactor/redefined-variable-type.html https://pylint.pycqa.org/en/latest/user_guide/messages/refac...
- maleldil 1y agoThis is only for redefinitions that change the type. If you re-assign with the same type, there's no warning. However, pylint does issue warnings for other interesting cases, such as redefining function arguments and variables from an outer scope.
- MrNet32823 1y agoJonathan Blow had strong objection with const keyword. I forgot because i did not understand at that time. Does anyone with jai experience have a counter point to that.
- dude250711 1y agoHe also stopped shipping things.
- JonChesterfield 1y agoI believe the standard counterargument goes: - either it's transitive, in which case your type system is very much more complicated - or it isn't, in which case it's a near useless liability Naturally C++ runs with the latter, with bonus extra typing for all the overloads it induces.
- maleldil 1y agoHow isn't it transitive in C++? If the variable/reference is const, you can't modify fields, and you can only call const methods. What else do you need?
- sunrunner 1y agoI like the idea of immutable-by-default, and in my own musings on this I've imagined a similar thing except that instead of a mutable keyword you'd have something more akin to Python's with blocks, something like: # Immutable by default x = 2 items = [1,2,3] with mutable(x, items): x = 3 items.append(4) # And now back to being immutable, these would error x = 5 items.append(6) I have put almost zero thought into the practicality of this for implementation, the ergonomics for developers, and whether it would be different enough and useful enough to be worth having.
- pizza234 1y agoThis is in essence a mutable borrow - by looking at Rust's borrow checker, one can see the complexities of the concept.
- danenania 1y agoClojure has transients—a similar idea I believe. Basically bounded mutation.
- unrealhoang 1y agoWithout a borrowck, inside your mutable block, another variable can reference to the mutable version of your x or items, and be mutated outside of that block.
- teo_zero 1y agoNo if you're allowed to only get an immutable reference from an immutable variable.
- DonHopkins 1y agoDid you ever have one of those days when variables won't and constants aren't?
- kybernetyk 1y agothe comments/replies to his tweet remind me why I usually avoid twitter
- koolba 1y agoThis is the mental distinction of what something represents vs what is its value. The former should never change regardless of mutability (for any sane program…), and the latter would never change for a const declaration. The value (pun intended) of the latter is that once you’ve arrived at a concrete result, you do not have to think about it again. You’re not defining a “variable”, you’re naming an intermediate result.
- smallstepforman 1y agoThere are languages nobody uses, and languages people complain about. Computing is about change, otherwise there is nothing to compute. The mere fact that its called a “variable” makes it obvious that its supposed to change.
- butokai 1y agoThis is a viewpoint commonly held by students who were exposed to imperative programming before having any class in maths. However it shouldn't survive long after that.
- tialaramex 1y agoBjarne's excuse is very silly, it's like the Laffer curve but for programming language defects. It pins one edge case nobody cares about, then tries to imply that's proof for a claim no sane person could agree with and for which there is no evidence. Bjarne says languages with no users attract no complaints regardless of how terrible they are (nobody was arguing that they do), therefore implies Bjarne, the fact that people complain about my language just means it is popular. Bzzt, wrong. They're complaining because it's so riddled with problems. Variables are distinct from constants. It's a problem that C and C++ use the keyword "const" to signify immutability instead, indeed as a result C++ needed three more keywords "constexpr", "constinit" and "consteval" to try to grapple with the problem.
- sgarland 1y agoDumb question from a primarily Python programmer who mostly writes (sometimes lengthy) scripts: if you have a function doing multiple API calls - say, to different AWS endpoints with boto3 - would you be expected to have a different variable for each response? Or do you delete the variable after it’s handled, so the next one is “new?”
- TZubiri 1y agoI think renaming an old variable is a common and sensible way to free a resource in python. If there are no valid names for a resource it will be garbage collected. Which is different in languages like C++ with manual memory management. John Carmack is a C++ programmer apparently that still has a lot to learn in python.
- sswatson 1y agoIn the vast majority of cases, developer ergonomics are much more important than freeing memory a little earlier. In other scenarios, e.g., when dealing with large data frames, the memory management argument carries more weight. Though even then there are usually better patterns, like method chaining. FYI John Carmack is a true legend in the field. Despite his not being a lifelong Python guy, I can assure you he is speaking from a thorough knowledge of the arguments for and against.
- TZubiri 1y ago>developer ergonomics are much more important than freeing memory a little earlier Preach to the python choir bro, but it should be telling when a python bro considers it's too ergonomic and wasteful. At some point being clean and efficient about the code is actually ergonomic, no one wants to write sloppy code that overallocates, doesn't free, and does useless work. To quote Steve Jobs, even if no one sees the inside part of a cabinet, the carpenter would know, and that's enough. tl;dr: Craftmanship is as important as ergonomics.
- sswatson 1y ago
- jodleif 1y agoThis Carmack guy knows his stuff.
- KaiserPro 1y agoI don't mean to start a holy war, thats not the point, but isn't this a side effect of C++ footguns rather than python allowing you to be lazy? I mean there is good reason to keep variables well scoped, and the various linters do a reasonable job about scope. But I've only really know C++[1] people to want everything as a const. [1] Yes, you functional people also, but, lets not get into that.
- cfontes 1y agoHe will like RUST very much.
- galangalalgol 1y agoCarmack 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.
- metaltyphoon 1y ago> 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.
- galangalalgol 1y agoThere isn't an enforced one, and the opinion is bifurcated, but I regularly find crates that clearly treat this as idiomatic practice.
- mavhc 1y agoReally should invent a new name if you don't want your variables to vary
- agentultra 1y agoAgree. After working seriously on a large production Haskell codebase for several years I definitely took it for granted. Now that I’m writing stuff in C again I do think immutability should be the default. const isn’t really it though. It could go further.
- nixpulvis 1y agoAre Rust's defaults far enough?
- 1718627440 1y agoWell in C actually you can not mutate something, you can only reassign, as it is always pass-by-value. You need to work around that, by passing a pointer to the object instead. In that sense mutability is kind of a language keyword: '&'. When you want to just get the object, you pass object it, if you need to modify it, you need to pass &object. This is something I hate in C++, that random function invocations can mutate arguments without it being obvious in the call syntax.
- astrobe_ 1y agoI think that's why the * is generally preferred over the & for this purpose. It also can give some hints about ownership issues. This "pass by reference" thing is syntactic sugar and sometimes is great to have, but as Perlis said, "Syntactic sugar causes cancer of the semicolon" [1]. [1] https://www.cs.yale.edu/homes/perlis-alan/quotes.html https://www.cs.yale.edu/homes/perlis-alan/quotes.html
- theodorethomas 1y agoDo as much as you can in a spreadsheet, then start a new spreadsheet.
- Const-me 1y agoFor performance critical code, you want to reuse L1D cache lines as much as possible. In many cases, allocation of a new immutable object boils down to malloc(). Newly allocated memory is unlikely to be found on L1D cache. OTOH, replacing data in recently accessed memory and reusing the memory is very likely to become L1D cache hit in runtime.
- ramses0 1y ago"Immutability" from a programming language statement perspective doesn't necessarily imply that the implementation duplicates memory or variables. Similar to how "tail recursion can (usually) be lifted/lowered to a simple loop...", immutability from language statements can often be "collapsed" into mutating a single variable, and there may be one or two "dances" you need to do to either add helper functions, structure your code _slightly_ differently to get there, but it's similar to any kind of performance sensitive code. Example foo(bar(baz(), bar_alt(baz_alt(), etc...))) [where function call nesting is "representing" an immutability graph] ...yeah, that'd have a lot of allocations and whatever. But: foo().bar().bar_alt().baz().baz_alt().etc(...) you could imagine is always just stacking/mutating the same variable[s] "in place". ...don't get hung up on the syntax (it's wildly wrong), but imagine the concept. If all the functions "in the chain" are pure (no globals, no modifications), then they can be analyzed and reduced. Refer back to the "Why SSA?" article from a week or two ago: https://news.ycombinator.com/item?id=45674568 https://news.ycombinator.com/item?id=45674568 ...and you'll see how the logical lines of statements don't necessarily correspond to the physical movement of memory and registers.
- Const-me 1y agoYou’re describing an edge case. Generally speaking, memory is only reused after old objects are deallocated. And here’s the relevant quote from the OP’s post: > Having all the intermediate calculations still available is helpful in the debugger
- marginalia_nu 1y agoFor performance critical code, you wouldn't use malloc()-allocation at all, though whether using an arena allocator or putting stuff on the stack, your argument is still sane. Data locality is speed.
- xd1936 1y agoWouldn't this allocate wasteful amounts of RAM unnecessarily for every step in a calculation?
- beeflet 1y agonot if it gets optimized out
- duxup 1y agoI'm going to maybe out myself as having limited experience here ... I don't mind the idea here, seems good. But I also don't move a block of code often and discover variable assignment related issues. Is the bad outcome more often seen in C/C++ or specific use cases? Granted my coding style doesn't tend to involve a lot of variables being reassigned or used across vast swaths of code either so maybe I'm just doing this thing and so that's why I don't run into it.
- Galanwe 1y agoSo essentially he gives 2 arguments: 1) You get intermediate results visible in the debugger / accessible for logs, which I think is a benefit in any language. 2) You get an increased safety in case you move around some code. I do think that it applies to any language, maybe slightly more so in C due to its procedural nature. See, the following pattern is rather common in C (not so much in C++): - You allocate a structure S - If S is used as input, you prefill it with some values. If it's used as output, you can keep it uninitialized or zfill. - You call function f providing a pointer to that struct. Lots of C APIs work that way: sockets addresses, time structures, filesystem entries, or even just stack allocated fixed size strings are common.
- d0mine 1y agoIn Python, no user object is modified by a simple assignment to a name. It just binds it. It is not about mutable/immutable objects , it is about using a name for a single purpose within given scope. a = 1 b = 2 a = b "a" name refers to the "2" object. "1" hasn't changed (ints are immutable in Python). If nothing else references it, it might as well disappear (or not). Though both "single purpose" and immutability may be [distinct] good ideas.
- deleted 1y ago[deleted]
- reverseblade2 1y agoUse F#, compile it to .net, rust, JavaScript, typescript and python. Problem solved.
- mcv 1y agoI use Javscript mostly. Or Typescript actually, these days. I remember when ES2015 introduced `let` because `var` had weird scoping issues. But ever since, I barely use either of them. Everything is `const` these days, as it should.
- rlander 1y agoExcept const is not sufficient. It will prevent the reference from being reassigned but the const can still reference a mutable object.
- andsoitis 1y ago> I use Javscript mostly. Or Typescript actually, these days. I remember when ES2015 introduced `let` because `var` had weird scoping issues. But ever since, I barely use either of them. Everything is `const` these days, as it should. reply const prevents reassignment of the variable but it does not make the object the variable points to immutable. To do the latter, you have to use Object.freeze (prevent modification of an object’s properties, but it is shallow only so for nested objects you need to recurse) and Object.seal (prevent adding or removing properties, but not changing them). May people use immutable.js or Immer for ergonomic immutable data structures.
- mcv 1y agoThat is an excellent point, and indeed a problem when debugging. When I log objects to the console, often they don't get serialized until I actually click on them, which means I don't get to see the object as it was at the time, but after a bunch of later changes.
- LogicHound 1y ago`var` doesn't have weird scoping issues, it just different than other languages. `var` is function scoped, thus all var declarations are hoisted to the top of the function during execution. This is why the single var pattern used to be recommended.
- hackthemack 1y agoOne area that I like to have immutability is in function argument passing. In javascript (and many other languages), I find it weird that arguments in function act differently depending on if they are simple (strings, numbers) versus if they are complex (objects, arrays). I want everything that passes through a function to be a copy unless I put in a symbol or keyword that it suppose to be passed by reference. I made a little function to do deep copies but am still experimenting with it. function deepCopy(value) { if (typeof structuredClone === 'function') { try { return structuredClone(value); } catch (_) {} } try { return JSON.parse(JSON.stringify(value)); } catch (_) { // Last fallback: return original (shallow) return value; } }
- jstimpfle 1y agoProblem is that "copy" of an object is not well defined. It could be a "shallow" copy or a "deep" copy. The only models where "copy" is defined are simple "memory"/"value" models (like C) and immutable models (like Haskell).
- hackthemack 1y agoIt is a similar idea to what Carmack is writing about. Golang, Clojure does something similar to what I am talking about above so not sure of the motivation behind the voting down of the comment.
- DougBTX 1y ago> I want everything that passes through a function to be a copy unless I put in a symbol or keyword that it suppose to be passed by reference. JavaScript doesn’t have references, it is clearer to only use “passed by reference” terminology when writing about code in a language which does have them, like C++ [0]. In JavaScript, if a mutable object is passed to a function, then the function can change the properties on the object, but it is always the same object. When an object is passed by reference, the function can replace the initial object with a completely different one, that isn’t possible in JS. Better is to distinguish between immutable objects (ints, strings in JS) and mutable ones. A mutable object can be made immutable in JS using Object.freeze [1]. [0] https://en.wikipedia.org/wiki/Reference_(C%2B%2B) https://en.wikipedia.org/wiki/Reference_(C%2B%2B) [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- oulipo2 1y agoCan we stop linking the racist website that twitter has become?
- AaronAPU 1y agoThis would require coming up with an order of magnitude more variable names which is just unnecessary cognitive load.
- jstimpfle 1y agoEither you don't understand what you're talking about, or you've missed the word "strive" in the tweet.
- chongli 1y agoNo, not at all. You still have the advantages of scopes, name shadowing, namespaces, and collection types. If your language supports them, you can also use algebraic data types to further reduce the number of names you need to deal with.
- furyofantares 1y agoAn 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 reassigning variables so much?
- AaronAPU 1y agoProbably 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 heavy code. You can avoid it with long expressions but that in principle is going against the “name every permutation” intention anyway.
- furyofantares 1y ago
- bmitc 1y ago> I wish it was the default, and mutable was a keyword. Well yea, that's what sane languages that aren't Python, C, and C++ do. See F# and Rust.
- nyrp 1y agoIs he referring to something specific with "true" iterative calculations vs. plain old iterative ones, assuming they are in some way "non-true" or less "true"? Like, avoiding i+=x in favor of ++i or something? Or maybe am I just tired today.
- ufo 1y agoI think he's just saying that mutation is ok if it's something loopy, like changing the loop counter or updating some running sum. So both i+=1 and ++i are fine.
- nixpulvis 1y agoI 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 for the intermediate result, I'll give it that, but I'm not over here naming things `result_without_processing`. You can read the code.
- furyofantares 1y agoYou'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.
- nixpulvis 1y agoI mean, I use `result` in a function named `generate` within a class `JSON < Generator`. Stuff like this is pretty common.
- philipov 1y agoif you're already committing to generic names, what's wrong with a name like `processed_result`?
- snarfy 1y agoIn the flow he describes you end up with processed_processed_processed_result.
- WhyNotHugo 1y agoJava mentioned!
- anymouse123456 1y agoI 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?
- deleted 1y ago[deleted]
- deleted 1y ago[deleted]
- tialaramex 1y agoIn 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 (for this piece of code) always saying $10. That's not a variable it's a constant. In Rust we'd use `const` for this but in C you need to use the C pre-processor language instead to make constants, which is kinda wild. However for each time this function runs we do also need to get the customer ID. The customer ID will vary each time this function runs, as different customers check out their purchases, but it does not mutate during function execution like that total earlier, in Rust these variables don't need an annotation, this is the default. In C you'd ideally want to label these "const" which is the confusing name C gives to immutable variables.
- ajross 1y ago> 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 function being expressed. Computers are discrete devices and a variable that "varies" across its domain inherently implies that it's going to be computed more than once. The sense Carmack is using, where it is not recomputed and just amounts to a shorthand for a longer expression, is a poor fit. I do think this is sort of a wart in terminology, and the upthread post is basically right that we've been using this wrong for years. If I ever decide to inflict a static language on the masses, the declaration keywords will be "def" (to define a constant expression) and "var" (to define a mutable/variable quantity). Maybe there's value in distinguishing a "var" declaration from a "mut" reference and so maybe those should have separate syntaxes.
- wodenokoto 1y agoMutability was by far the most difficult thing when learning Python and mutating objects by iterating over its items do get confusing, even as a senior. When I was first learning I thought all methods would mutate. It has a certain logic to it
- storus 1y agoI had enormous fun optimizng C++ via self-modifying assembly to squeeze the utmost performance of some critical algorithms, and now this drive towards immutable everything feels like cutting my hands and legs off and forcing me to do subpar engineering.
- ryandrake 1y agoSame here, I grew up in a world where you had a handful of registers and a bunch of memory locations, and those were your variables. clc lda value adc #1 sta value lda value+1 adc #0 sta value+1 value .byte 0,0 These constraints are pretty much built into my concept of programming and it would take great effort to break out of it. It feels nice doing: x = 20 x = x + func(y) x = x / func(z) And it would feel weirdly wasteful to do: x = 20 x1 = x + func(y) x2 = x1 / func(z) Like, I know that variables are no longer a scarce resource, but my brain still wants to conserve them.
- 1718627440 1y agoTo me this also seem to be wasteful, even if not for the computer, but it wastes my amount of working state I can keep in my head, which is very limited.
- restalis 1y agoI was thinking about this too. When you read a program, there is this information payload, which is the metaphorical ball you have to keep your eyes on, and more or less forget about the rest as soon as it isn't relevant any more. In the functional paradigm it's like seeing the juggle of a bunch of such balls instead (plus the expectation to admire it), but that's just wasteful on reader's attention.
- no_wizard 1y agoCompiler optimizations make up for it, usually. Thats been my experience at least. An important thing to keep in mind is just how far compilers have come over the least 40 years. Often, with immutable languages, they can do extremely efficient compile only optimizations because of said guaranteed immutability by default. Its not true in every case, of course, but I think for most situations compilers do a more than good enough job with this.
- warmwaffles 1y agoThis shouldn't be a hard and fast rule for everything. Be treated as guidelines and allow the programmer some wiggle room to reuse variables in situations that make sense.
- AnotherGoodName 1y agoGoing too hard on mutation means you usually end up with larger structures that are recreated completely. Those themselves are then the point of mutation. This can be helpful if the larger object needs a lot of validation and internal cross rules (eg. You can't set 'A' if 'B' is true, you can validate that when recreating the larger object whereas if 'A' was mutable on it's own someone might set it and cause the issue much later which will be a pain to track down). Anyway the outcome of trying to avoid mutation means instead of simply setting player.score you get something like player = new Player(oldPlayerState, updates). This is of course slow as hell. You're recreating the entire player object to update a single variable. While it does technically only mutate a single object rather than everything individually it's not really beneficial in such a case. Unless you have an object with a lot of internal rules across each variable (the can't be 'A' if 'B' example above) it's probably wrong to push the mutation up the stack like that. The simple fact is a complex computer program will need to mutate something at some point (it's literally not a turing machine if it can't) so when avoiding mutation you're really just pushing the mutation into a higher level data object. "Avoid mutations of data that has dependencies" is probably the correct rule to apply. Dependencies need to be bundled and this is why it makes sense not to allow 'A' in the above example to be mutated individually but instead force the programmer to update A and B together.
- acdbddh 1y agoIn python its common to see code like this: df = pd.concat(df,other_df) df = df.select(...) ... My eyes hurts, when I see it. It makes me avoid python. I bet the reason for this mutable code is a missing simple pipes syntax. I love pipes. In R can do: df |> rbind(other_df) |> select(...) It feels much better.
- mr_luc 1y agoElixir too (Explorer library; default backend is Pola.rs based) - https://github.com/elixir-explorer/explorer https://github.com/elixir-explorer/explorer - https://hexdocs.pm/explorer/Explorer.html https://hexdocs.pm/explorer/Explorer.html
- fermisea 1y agopandas has a .pipe operator which works exactly like this
- cubefox 1y agoWouldn't that be simply: df = pd.concat(df,other_df).select(...)
- leviathan 1y agoMy eyes would hurt more if I had to look all day at the vertical misalignment of that |> operator
- kec 1y agoYou could always switch to a better font like Fira Code which has a ligature for this.
- adregan 1y agoThe gp's comment wasn't made regarding the look of the operator in its ascii representation `|>` but about the vertical misalignment. Typically you align a pipeline like so: df |> rbind(other_df) |> select(...) But these topics are largely left to code formatters these days.
- andsoitis 1y agoIs there another programming language that comes close to Clojure’s persistent data structures?
- TheRoque 1y agoI also default to const in javascript. Somehow a "let" variable feels so dirty, but I don't really know why. I guess at this point my experience forged an instinct but I can't even put a theory on it. But it's sometimes necessary and I use it of course.
- catlifeonmars 1y agoIn JS, by using const, you are signalling to the reader that they don’t need to look out for a reassignment to understand the code. If you use let, you are saying the opposite.
- maleldil 1y agoJS's const doesn't go far enough since you can still mutate the object via its methods. In C++, you can only call const methods (which can't mutate the object) on const variables.
- bitbasher 1y ago> ... making almost every variable const at initialization is good practice. I wish it was the default, and mutable was a keyword. Rust mentioned!
- RamtinJ95 1y ago[dead]
- willismichael 1y agoHere's a relevant comment on Rust: https://x.com/ID_AA_Carmack/status/1094419108781789184 https://x.com/ID_AA_Carmack/status/1094419108781789184
- maleldil 1y agoThat was 6 years ago. I'd like to see how that feeling developed.
- scuff3d 1y agoAnd Zig :)
- alternatex 1y agoPlus F# and a whole family of FP languages.
- shermantanktop 1y agoConst/final by default - preach it, brother, amen and hallelujah. Mutability by default is the rule in the big languages and the world would be a better place if it weren’t.
- textlapse 1y agoI wish C++ did some sane things like if I have a const member variable, allow me to initialize it as I wish in my constructor - it's a constructor for crying out loud. Don't be silly and assume if I assign it multiple times in an if condition it's mutable - it's constructing the object as we speak, so it's still const!!! C# gets this right among many other things (readonly vs const, init properties, records to allow immutability by default). And the funny thing is the X thread has lots of genuine comments like 'yeah, just wrap a lambda to ensure const correctness' like that's the okay option here? The language is bad to a point it forces good sane people into seeing weird "clever" patterns all the time in some sort of an highest ELO rating for the cleverest evilest C++ "solution". I was hoping Carbon was the hail mary for saving C++ from itself. But alas, looks like it might be googlified and reorged to oblivion? Having said that, I still like C++ as a "constrained C++" language (avoid clever stuff) as it's still pretty good and close to metal.
- QuadrupleA 1y agoLove Carmack, but hard disagree on this and a lot of similar functional programming dogma. I find this type of thing very helpful: classList = ['highlighted', 'primary'] if discount: classList.append('on-sale') classList = ' '.join(classList) And not having to think about e.g. `const` vs `let` frees up needless cognitive load, which is why I think python (rightly) chose to not make it an option.
- ColeShepherd 1y agoSome potential alternatives to consider: 1. classList = ['highlighted', 'primary'] .concatif(discount, 'on-sale') .join(' ') 2. classList = ' '.join(['highlighted', 'primary'] + (['on-sale'] if discount else [])) 3. mut classList = ['highlighted', 'primary'] if discount: classList.append('on-sale') classList = ' '.join(classList) freeze classList 4. def get_class_list(discount): mut classList = ['highlighted', 'primary'] if discount: classList.append('on-sale') classList = ' '.join(classList) return classList classList = get_class_list(discount)
- rudy6912 1y agoFennel (Lisp): (table.concat [:highlighted :primary (if discount :on-sale)] " ")
- deleted 1y ago[deleted]
- zahlman 1y ago> 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.
- williamdclt 1y agoIt helps with that, but it has other trade-offs: indirection isn't free for readability.
- throw10920 1y agoI'll go further than that and say that indirection significantly increases cognitive load and hurts readability.
- zahlman 1y agoI 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.
- tengbretson 1y ago> 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.
- dinkblam 1y ago// !!!: SWIFTY VAR & LET SUPPORT #define var __auto_type #define let const __auto_type
- thefaux 1y agoEven better is to use tail calls instead of loops and eliminate mutable variables entirely.
- mtlmtlmtlmtl 1y agoIt's fascinating how even when Carmack says something rather obvious and unoriginal, that many people have said before, sometimes decades ago, it still spawns a 400+ comment thread on HN. I really don't get it, it's almost like a cult of personality at this point.
- kins 1y agoIt always was.
- GTP 1y agoIn other words, he wishes he used Rust.
- groby_b 1y agoIt is really amazing in how many ways C/C++ made the wrong default/implicit choices, in retrospect. Hindsight's 20/20, of course. But still.
- rezonant 1y agohttps://xcancel.com/id_aa_carmack/status/1983593511703474196 https://xcancel.com/id_aa_carmack/status/1983593511703474196
- seattle_spring 1y agoCMV: HN should just automatically replace x links with xcancel
- avadodin 1y agoWe're all already doing this as the compiler turns everything into SSA form, silly goose.
- WalterBright 1y agoI'm curious where this fits in with single assignment semantics: int x = 3; x = 4; // error! int* p = &x; *p = 4; // is that an error?
- taf2 1y agoAt least with clang it's a warning: f.c:4:8: warning: initializing 'int *' with an expression of type 'const int *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] 4 | int* p = &x; | ^ ~~ 1 warning generated.
- wicharek 1y agoYes, because line 3 would implicitly be: const int * p
- deleted 1y ago[deleted]
- waffletower 1y agoWould be beneficial if Rich Hickey's opinion and experience regarding mutable state was given more weight than John Carmack's.
- carabiner 1y agoDoes anyone have any real naming conventions, patterns for doing this in ds programming in notebooks? I've got a bad habit of doing: df = pd.read_excel() df = df.drop_duplicates.blahblah_other_chained_functions() [20 cells later] df = df.even_more_fns()
- armaoin 1y agoTotally agree. const auto for me is the default.
- throwaway984393 1y ago[dead]
- markstos 1y ago"State is the enemy". Every new state is an additional condition to check. A mutated variable implies an additional state to test. One boolean means two states to test. Four booleans that interact is 2^4 (16) states to test.
- baruchel 1y agoWhich is why functional programmers believe in the separation of Church and state. https://wiki.c2.com/?SeparationOfChurchAndState https://wiki.c2.com/?SeparationOfChurchAndState
- AtNightWeCode 1y agoI care less and less about things like this. At some point you will write code in some lang that have fancy keywords and stuff gets mutated anyway. Also, what people tends to do if stuff is immutable is that they hide mutation by doing deep copies with changes.
- moi2388 1y agoI agree. I mainly write c# nowadays, but even there I try to make everything as immutable as possible. Makes everything so much easier to reason about.
- garrison 1y agoThis reminds me of an earlier blog post by the same author: https://web.archive.org/web/20121111234839/http://www.altdevblogaday.com/2012/04/26/functional-programming-in-c/ https://web.archive.org/web/20121111234839/http://www.altdev...
- vezcha 1y agoThe efficiency advantage of immutability needs to be made more transparent and understood by engineers. Memory flexibility and recyclability have potential costs and should be minimized as much as possible. More importantly, mutability is often unnecessary.
- acdbddh 1y agorelated quote from Carmack: > "A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in." https://web.archive.org/web/20201229045320/https://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php https://web.archive.org/web/20201229045320/https://www.gamas... And variable mutations increase the cognitive load necessary to understand all the possible states.