Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is pretty common when adding immutability to a language that wasn't designed around it. Whether it's JavaScript's `const` or Java's `final`, languages almost always add shallow immutability rather than deep. My sense is that it's quite difficult to add deep immutability in later.

Rust is the main exception I'm aware of, and it had deep immutability from the get-go.



Scala has immutable types in the standard library too, but I don't think they make much sense without the language being statically typed. By which I mean adding immutable types to Python or whatever's stdlib would not be that hard, but it would most likely be confusing since you don't know for sure what you have until you look at it and see.


Rust's deep immutability is different than most in that it allows setting a reference as recursively immutable even if the data type it's pointing to has mutable fields.

Scala gives you immutable data types but unless I'm mistaken there's no way to say "recursively disable mutating functions for all elements in this list"—you just have a list that cannot be mutated. If you stick a Java object in a Scala list, it's liable to mutate, so Scala has shallow immutability.


Yeah that's true, it is different. Neat :). With Scala you would put immutable types in your list if you wanted to, but I get what you mean, it's not the same.


Scala's immutable types are also "shallow". For example you can put mutable objects inside an immutable container and change them when you want. Rust avoids this using lifetimes.

It would be interesting if mutability was actually part of Scala's type system, for example if immutable Seq was defined as

    class Seq[T <: Immutable]


> I don't think they make much sense without the language being statically typed.

They absolutely do, in part because you son’t even have a type system you’d need to undermine in order to achieve mutation.

There are “immutability first” dynamically typed langages, and they work rather well (erlang, clojure).


> Whether it's JavaScript's `const`

const is JS is referring to the reference and has nothing to do with the value, it's not even shallow immutability, there is none at all.

    const arr = []
    arr.push(1)
is valid JavaScript.

Bit nitpicky maybe, but wouldn't want people to get the impression const gives you any sort of immutability.


> wouldn't want people to get the impression const gives you any sort of immutability.

const makes the binding immutable but not the object. Object.freeze makes the object immutable but not it’s children.


Accidentally mutating globals was a big problem with the language. Const didn't solve it completely: you can still implicitly create globals by forgetting to declare the binding locally. But at least if you have a global you can make it an immutable binding and get warned about mutating it some of the time (ie assignments). As far as I'm concerned that is all it is useful for. For a local binding you don't really get much benefit since it's not deeply immutable, and it isn't an improvement to the ambiguity of local/global scopes, so you're frankly better off not using it as it will trick newcomers.

The same argument doesn't apply to Data's shallow immutability. It will give you errors when mutating at least some of the fields. If your code can catch you mutating a number, then you can notice the bug and be reminded to make deep copies etc. It's an improvement, just like Object.freeze.


It's really the same problem extended to attributes of Data. The "immutability" in Data _also refers to the reference_.

To take Python tuples (which are morally pretty close to Data)

  my_data = ([],false)
  my_data[0].push(1)
tuples are immutable, but this happens, because the "immutability" is in the same vein as JS's const: "we are always pointing to the same object".


This is exactly the same problem that OP was drawing out with Ruby's Data: the references inside the Data object are immutable and cannot be changed to point to other objects, but once you've dereferenced the (immutable) pointers there are no immutability guarantees. Hence, shallow immutability.

The only conceptual difference between this and JS's `const` is that you can't use `const` to declare object properties immutable; for that you need Object.freeze().


For JavaScript, there's an active proposal to add deeply immutable objects and arrays: records and tuples. [0]

[0]: https://github.com/tc39/proposal-record-tuple/


> Rust is the main exception I'm aware of, and it had deep immutability from the get-go.

C and C++ have deep immutability too, but both come with many other footguns as baggage.




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

Search: