Open DartBot opened 12 years ago
Deep immutability is a very useful concept. Unlike shallow immutability (final) it isn't a property of a variable however - it is a property of an object. It just won't be useful if another reference to that object can make changes to it.
Deep immutability is a complex issue. It would be nice to have, but it will take some time to see if we can come up with a workable solution.
Set owner to @gbracha. Removed Type-Defect label. Added Type-Enhancement, Area-Language, Accepted labels.
Changed the title to: "Value types (deep immutability)".
This comment was originally written by mattsh@google.com
Just to clarify or emphasize what's already stated above, immutability and readonly are different concepts. (You might have a readonly view of an object that can be mutated by something else.)
Also, related, we currently have interface FrozenElementList used by some DOM APIs.
This comment was originally written by domi...@google.com
Thank you for clarifying. My original intent was for readonly as a property of a var. Ie, you can create a readonly reference for the scope of a method, or you could define a class member as readonly which would require it to be set at construction.
Creating a non-readonly reference to a reference that has been marked readonly would be a compile time error (as I believe it can be statically determined). Similarly, calling non-readonly methods on a readonly reference would be an error, though I'm not sure if that can be determined statically without some annotation from the developer on the method.
This comment was originally written by @yjbanov
I think it is worth a consideration, although I'd recommend the more familiar keyword "const" rather than "readonly", since one of the major goals is to keep the language as familiar as possible to programmers of existing languages, in this case C#/C++.
A related topic is read-only API for certain core Dart classes. I posted a proposal (includes a working prototype) for a read-only API for some of Dart's core collections:
Which grew out of this long discussion:
Yegor
Added this to the Later milestone.
This comment was originally written by @polux
I would be so glad to see this happening. Here are some thoughts:
- Dart could provide deep immutability by construction (you can only construct a deeply immutable value from deeply immutable values), and this could be enforced at runtime, even in unchecked mode. The downside of this approach is that it requires a very functional style and makes the building of graphs almost impossible (unless you comme up with some notion of lazy values, I guess).
- Alternatively, we could have a "freeze" operator which would mean "from now on, this value is deeply immutable". Then checked mode would check that none of its recursively subvalues is ever modified. I guess that would be very costly but on the other hand it would offer an unprecedented flexibility.
- A variation around the alternative is to freeze by copy, which would make the subsequent checks much easier, but would induce possibly unbearable runtime costs.
Removed this from the Later milestone. Added Oldschool-Milestone-Later label.
Removed Oldschool-Milestone-Later label.
FYI https://github.com/google/built_value.dart is a library that uses codegen to support value types. If you use it for all your types, you have deep immutability.
Reviving an old thread. We use built_value, but we also like free-handing objects. With NNBD, I'm planning to roll out some best practices to our team for free-handed immutables:
class Foo {
final int bar;
Foo(this.bar) {
// hashObjects([bla, bla]) from quiver library
hashCode = bar.hashCode;
}
/// The `late` keyword is coming with NNBD.
@override
late final int hashCode;
@override
bool operator ==(dynamic other) =>
// Short-circuit identical objects. BTW, BuiltValue does this already.
identical(other, this) ||
(other is Foo &&
// Short-circuit unequal objects.
other.hashCode == hashCode &&
// Proceed to check each value individually.
other.bar == bar);
}
I wonder if it's possible to create a language syntax that does the same thing?
immutable class Foo {
final int bar;
Foo(this.bar);
}
FWIW, and as the author of built_value
I'm almost certainly biased ;) I don't think NNBD is enough to make hand-maintaining immutable value types a good idea. I wrote an article about all the problems with hand maintained immutable value types ;)
https://medium.com/@davidmorgan_14314/the-mutability-tax-6403d84f21c0
This issue was originally filed by domi...@google.com
I would like to suggest the addition of 'readonly' as a keyword to complement 'final'. It's use should instruct the compiler to enforce the deep immutability of the variable it describes. This is useful to people using an API, and provides strong hints for optimization possibilities for the compiler and VM.
For example, this is legal:
Bar mybar; foo(mybar);
void foo(final Bar bar) { bar.thing = other_thing; }
I suggest that:
Bar mybar; foo(mybar);
void foo(readonly Bar bar) { bar.thing = other_thing; }
should be illegal and should error during compilation when assignment to bar.thing is attempted.
Similarly, any attempt to take a non-readonly reference to a readonly variable should be illegal.
This may require methods on an object to be marked as readonly to indicate that they don't mutate |this|, though it may be possible to statically determine this.