Hacker News new | past | comments | ask | show | jobs | submit login

Great! Now can we make `final` the default for all fields, variables, and parameters?

(yes yes, I know, that would break syntax... but please come up with something to discourage mutability)




Const-ness in C++ is something I miss in other languages. Being immediate able to see that this function or method couldn't mutate the object made it so much easier to reason about the code.

Yeah I know there's ways around it, but then the author known what they told the other party to expect.


Yeah I find it a bit startling going from rust (where const is the default) to basically any other language. Sometimes I look at typescript function definitions and I’m like - uuuuhhh does this function mutate that parameter? Does it keep a reference to it? If the object I’m passing is mutated after I call this function, will something break? It’s impossible to tell from a function signature, even with all of typescript’s type safety. That gives me the willies. - and for good reason, it’s tripped me up lots of times.

Even the JS standard library struggles with this. You just have to remember that .sort() modifies the array in place (and returns it), but .slice() does a shallow clone of the array. (Not a deep clone - that would be different again!)


> Even the JS standard library struggles with this. ...

I don't think this represents struggling. There needs to be some way to sort in place. Sometimes you need to sort a big array and don't want to allocate.

And there should be some way to clone the array without mutating. That's slice. So you how do you sort a clone? .slice().sort()

I think by far the biggest problem with ES .sort() is that number arrays don't sort numerically by default.


> There needs to be some way to sort in place. Sometimes you need to sort a big array and don't want to allocate

Your parent isn't saying there shouldn't be mutation, just that the mutation should be obvious.

In Rust, the type signature for the in-place sort is

  pub fn sort(&mut self)
  where
      T: Ord,
That `&mut self` lets you know that it's going to mutate.


Exactly. And for function parameters, rust also (usually) makes it obvious when you’re passing by value, by reference or by mutable reference:

    foo(x); // Moves or copies
    foo(&x); // immutable reference
    foo(&mut x); // mutable reference
I don’t need to look up the signature of foo to understand what happens to my variable. It’s obvious at a glance.


Fair point. I guess this is the cost of dynamic languages.


> Even the JS standard library struggles with this. You just have to remember that .sort() modifies the array in place

ISTM that there are almost always some kinds of mutable data structures present in non-trivial programs. And outside of the program, you have databases to directories of files that get mutated by users or other parts of a program. I think this is just a fact of life.


The point isn't that there's mutability, it's to be able to easily identify where it is and where it is not.

Languages which lack the tools to do this are just harder to reason about, at least for me.

In the JS example, as a non-JS coder, I would expect that a sort function that returns nothing/void would sort in-place, while a sort function that returns an array would return a sorted copy. I would not expect it to sort in-place and return it.

However in C++ I could easily see from the function definition that it might be doing that, because a sort that returns a copy would take a const reference for the input array. So if I came across a sort function which took a non-const reference as input I'd be able to at least suspect it's doing it in-place.


If possible, 1) design completely immutable data structures that can be broadly shared and don't need to be copied. If you need mutability, just embrace the fact that someone is going to abuse mutability and 2) try to create abstractions that can suffer abuse. If you're coming from a language that doesn't have const, you learn to build things that are hard(er) to screw up.

While bad code can exist in any language, I get worried about too much const in code, because it means they failed at both 1) and 2) and instead there are usually seriously tricky protocols that must be observed to make the thing work. I often ran into code where people were sprinkling const all over the code to lock things down but they fundamentally did not understand the design and made it nearly impossible to evolve, unless you used casts to get rid of const, which defeats the whole purpose.

I'm not saying const doesn't have value, but it's weapon #3, not weapon #1.


> sprinkling const all over the code to lock things down

That's like using a hammer on a screw, clearly not the right way.

Thankfully I've never worked on such codebases.


D goes one step further, as const is transitive.


const is weird and I see no way of getting it right. Making const transitive in the way described in tha Dlang reference certainly sounds weird. It prevents totally valid use cases. Why wouldn't I have a const pointer to a mutable thing?

The real problem is that it's not defined what is an object, and where, in a web of memory-objects pointing to each other, a conceptual object begins and ends. It's arbitrary. Also, what is const from one standpoint is not const from another. It's fluid. Type systems generally don't play well with that. The weirdness of the original `strstr()` C API showcases that problem well.

The only thing that makes some sense here is to keep it simple -- any struct-like thing is an individual object. Embedded objects of course inherit const-ness, but pointed-to objects do not.

I think the C++ model is also somewhat right -- any class can define individually through which indirections const-ness follows through. But the C++ way causes lots and lots of boilerplate.

In any case, my solution is to use const very sparingly, it can hardly be gotten right in practice beyond basic use. Being more rigid of const may have prevents 2-3 bugs in my life, but cost sooo much more time and required many rewrites to make it fit somehow.

I use const mostly for

    - static const data
    - some string-slice datatypes (these are often initialized from static const data like string literals).
    - read-only function parameters passed by reference (Foo const *thing).


Hence why D has two approaches with similar semantics, const and immutable.

And also allows to alias non const references to the same data.

Thus the data can be modified by a mutable reference, owned by a specific part of the code, while everyone else only gets to see the const chain.


I'd be happy with a `val` keyword. I thought that was a seriously missed opportunity when they introduced `var`.


>but please come up with something to discourage mutability)

Records?


  What man that sees the ever-whirling wheel
  Of Change, the which all mortal things doth sway,
  But that thereby doth find, and plainly feel,
  How Mutability in them doth play
  Her cruel sports to many men's decay?
(Edmund Spenser, 1596)


Records in Java have final fields.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: