4 ms·
> When I write code, it's common to have references to immutable classes thrown around with wild abandon, heedless of ownership, threads, or good taste, because
by cbsmith 4y ago
> When I write code, it's common to have references to immutable classes thrown around with wild abandon, heedless of ownership, threads, or good taste, because the data just can't change.
If there's anything I wish languages with implicit reference semantics would adopt, it's implicit immutability. I wish Java would be so much nicer with keyword that is half way between "final" and "volatile" that means, "yes, you can actually mutate this" and then make final semantics the default for fields & variables.
- cogman10 4y agoAgreed. Could you Imagine a Java where you have a `Map` and a `MutableMap` and that's what you put at your API? I'd make it SO much clearer how safe any individual API is to call.
- foverzar 4y agoKotlin has this
- thomascgalvin 4y agoKotlin has this, but the Map is (usually) a MutableMap under the covers, because it's Java bytecode at the lower levels. You have to go out of your way to footgun yourself, but it's still possible.
- morelisp 4y agoIn general this doesn't work, the history rule says mutable types are not proper subtypes of immutable ones (and the converse is obvious). If you want to capture mutability in your type system, it needs to be orthogonal to subtyping (like C/C++ const).
- deleted 4y ago[deleted]
- a_t48 4y agoC++ still has this problem - std::unordered_map<std::string, std::string>` and `std::unordered_map<std::string, const std::string>` are basically unrelated types - you can't const-cast the templated const away. (I may be misunderstanding here)
- masklinn 4y ago> you can't const-cast the templated const away. That seems like a good thing. If you're handed a map to const values you can't just go "imma gunna mutate them anyway".
- morelisp 4y agoNormally (when containers are not involved) this is exactly the point of a const cast.
- account42 4y agoNo, the primary point of a const cast is "I need to pass this to an API that expects a non-const pointer even though it won't mutate it". Casting const away and then mutating is a footgun as you are in undefined behavior territory as soon as your pointer/reference is not just const itself but points to a const variable.
- a_t48 4y agoYup, it's definitely a bit of a code smell if you do. The issue is more the reverse, though - I can't make a mutable map, then hand it by pointer/reference to something that says it wants an immutable map later.
- masklinn 4y agoNot necessarily a bad thing either, things can get odd if you're not the only owner and it's mutated under you unexpectedly.
- deleted 4y ago
- cogman10 4y ago> the history rule I'm unfamiliar with this rule (and not finding anything good to google). Can you elaborate? I can't really think of a scenario where an immutable datastructure isn't a subset of actions against a mutable datastructure.
- kapep 4y agoI had to look it up too, it apparently is a constraint for subtypes defined in Liskovs substitution principle [1]. From Wikipedia: > History constraint (the "history rule"). Objects are regarded as being modifiable only through their methods (encapsulation). Because subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. The history constraint prohibits this. It was the novel element introduced by Liskov and Wing. A violation of this constraint can be exemplified by defining a mutable point as a subtype of an immutable point. This is a violation of the history constraint, because in the history of the immutable point, the state is always the same after creation, so it cannot include the history of a mutable point in general. Fields added to the subtype may however be safely modified because they are not observable through the supertype methods. Thus, one can define a circle with immutable center and mutable radius as a subtype of an immutable point without violating the history constraint. [1]: https://en.wikipedia.org/wiki/Liskov_substitution_principle#Principle https://en.wikipedia.org/wiki/Liskov_substitution_principle#...
- aatd86 4y agoJust curious, isn't it obvious from a logical standpoint? I don't see how one could consider a mutable type to be a subtype of an immutable one. On the other hand, an immutable subtype of a mutable one seem plausible?
- cogman10 4y agoImmutable from a mutable is just as implausible. You cannot remove a method from a subtype which results in mutating methods being disabled through other means (like throwing exceptions). This is also a violation of LSP and the open close principle. Consider a `List` with an `add` function. What would you do with that `add` function to make an `ImmutableList` subtype?
- gowld 4y agoNot quite. Conceptually, you can have constness in your subtype-system (as long as you are sticking with interfaces (methods), as Liskov's subtyping model does, and aren't inherting potentially-mutable fields). MutableMap and ImmutableMap are both subtypes of a hypothetical ReadableMap. ImmutableMap is the same as ReadableMap, but has an informal contract that subclasses shouldn't add mutability.
- spockz 4y agoScala has had this for ages. You can have it today. Even in Java either through the Google collection library or through a library that mimics fp style programming. The name eludes me for the moment.
- gowld 4y agoGuava, but it's not fully typesafe. Mutable is not part of the Java meta-language used for typing. ImmutableMap implements Map https://guava.dev/releases/23.0/api/docs/com/google/common/collect/ImmutableMap.html https://guava.dev/releases/23.0/api/docs/com/google/common/c... and throws exceptions on mutation. https://guava.dev/releases/23.0/api/docs/src-html/com/google/common/collect/ImmutableMap.html#line.494 https://guava.dev/releases/23.0/api/docs/src-html/com/google...