-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Immutable Objects #7626
Comments
So this proposes to solve the self-mutating immutable Point point = Point.Empty;
point.Offset(2, 2); // compiler error, Point.Offset is not pure |
@HaloFour Yes. If |
|
|
@alrz I'm working with an existing example from the CLR. readonly Point point = Point.Empty;
point.Offset(2, 2);
Debug.Assert(point.X == 2 && point.Y == 2); // oops, point was changed If that was combined with some enforcement of pure functions then this situation could potentially be avoided. Where these kinds of proposals run into problems is that short of CLR changes all you have are compiler hints, which could very easily be ignored or faked. There's nothing that enforces that a method decorated with |
@HaloFour So I think it is actually good to make it a compiler error in an immutable context. Because it doesn't make sense and it is dangerous! "It's a problem for |
@alrz I'd largely agree. Even without CLR enforcement I think that immutable variables (and I imagine fields) could bring a lot of value. Now for the $64,000 question, since |
@HaloFour I'd rather choose same keywords for variables and types, const class Foo {
const Bar bar;
const Foo Baz() { ... }
} Because there is no reason to declare an |
|
What is the value of an immutable view onto a mutable object? An optimizer cannot eliminate reads because of aliasing. Thread-safety is not guaranteed by the same reason. C++'s About the technical side, how are we going to know for an existing object, which methods are safe to call? If we are going to require explicit marking, this seems to exclude the existing types from being used with the proposed feature. |
@vladd Aliasing wouldn't be a problem, because every |
@alrz But if so, how would However if we are talking about the types which are immutable intrinsically, they wouldn't need |
@vladd "The constructor is going to create a mutable object" creating a mutable object woudn't be a problem because pure functions cannot mutate them anyways. But it mustn't leak them beside of |
@alrz Well, we cannot be sure that the constructor doesn't leak And what about just simple |
@vladd If |
@alrz Okay, this is already better. But this means that the existing classes all need to be extended to mark the immutable constructors (or to add immutable ones and mark them, too), right? |
@vladd We've been already through this for the whole |
@alrz As for me, I see this feature as well better in the form of classes being completely immutable from scratch. Making "two classes -- normal and immutable -- on the price of one" does not seem to work. E.g., for immutable classes you need a different API: |
@vladd Then you woudn't be able to guarantee its immutability. This is the part that I directly stole from C++ and it seems OK. |
@alrz If a class declares itself to be completely pure/immutable, there shouldn't be a big problem for the compiler to ensure/enforce this property, right? (At least I believe so.) |
@vladd Yes, then every method has to be pure which will be verified in particular. And then all of them are callable from its instances. |
@HaloFour: Actually, your example doesn't work that way: using System;
using System.Drawing;
class Program
{
static readonly Point point = Point.Empty;
static void Main(string[] args)
{
point.Offset(2, 2);
Console.WriteLine(point);
}
} prints
This behavior is a minor source of inefficiency that could have been avoided had the proposal been implemented into the language from day one. I don't think that the behavior can be changed now, however, without any breaking changes. If the field were not |
@axel-habermaier However, same woudn't work with a reference type. This rule exists to make About Resharper, how it finds out about purity of functions? If it examines IL, well done! By the way, |
@axel-habermaier I was referring to I also wonder how R# detects this situation short of interpreting the IL of the method and any method it calls. |
@svick I've updated the point (2), I didn't quite get it at first. |
@alrz, @HaloFour: There is no magic involved with Reshaper: it relies on the Consequently, Resharper's solution is not as safe and convenient as C++'s |
@axel-habermaier Re "Jetbrains annotated most of .NET with the attribute" you mean outside of .NET framework itself, right? meaning that they have a list of (presumably) pure methods somewhere. That was my first guess. But I thought that if it's relying on IL it is indeed magic. |
@alrz: Yes, outside of .NET. No IL inspection is involved. |
i don't know if that would be plausible but it will be nice to expand "let" and "var" like swift. Generally i think it well be great if both "var" and "let" could be used for type inference as well as mutability and immutability respectively. Note : I am just starting with Roslyn and language grammars for that matter . so i don't know if this would be plausible or possible without breaking people but still it will be great if possible. |
@shahid-pk FYI, |
Why not reuse the "readonly" in variable declaration and change it's behaviour to also include true immutability ? readonly string ff; Having both readonly and immutable/let/whatever will just needlessly complicate things. |
If existing classes such as |
@aluanhaddad No, because CLR doesn't allow methods with the same name and signature, and also, since non-immutable objects are allowed to call methods regardless of their purity, you can't have an overload resolution based on it. |
@alrz Right, that's currently the case, I was asking if this proposal implied such a change. |
I believe you could use modopt and modreq to represent purity and have different methods with the signature only differing by purity. |
Closing in favor of readonly receivers (dotnet/csharplang#421) as it addresses use cases mentioned here. |
Immutable Objects
This proposal (inspired by C++
const
qualifier) can be considered as an extension to #159 utilizing #7561. I want to take that motivation further and suggest to enforce immutability through function purity and immutable variables. This would be yet another level of compile-time checking beside of #5032 — preventing mutation on variables, rather thannull
values.Immutable variables
Immutable variables/fields/params are a superset of
readonly
variables/fields/params (#115), therefore, anyimmutable
variable/etc is alsoreadonly
; so there is no need to explicitly declare them asreadonly
.An immutable variable can be declared as follow:
This is consistent with
from
andlet
syntaxes which permit an optional type rather thanvar
keyword.This feature enables us to guarantee an immutable view of a mutable type, e.g.
Array
orValueTuple
to avoid yet another immutable variant of these types, likeImmutableArray
orImmutableTuple
which is kinda nice.const
,immutable
variables are only allowed to call pure methods.Note that generated constructor and properties' getters of records and anonymous types are implicitly pure.
Pure methods
Original thread: #7561.
Therefore, marking a method or constructor as pure is not a breaking change if it is already pure.
With this feature we can, for example, define something like
ImmutableList<T>
on top of an existingList<T>
with pure methods:If a type didn't provide such methods, an extension pure method can be used as long as ctor is pure.
We would use the existing
PureAttribute
with actual purity enforcement; this could make already annotated code considered to be pure which consequently lessens the work to extending existing code to be compatible with this feature.Immutable types
Original thread: #159.
Following C#'s rule for
abstract
classes and methods,immutable
types require every field to be declared asimmutable
and constructors and methods have to be pure.Note that
Bar
is not essentially an immutable type, butbar
is an immutable variable.immutable
.There is no
immutable
generic constraint since you would be able to use immutable parameters.The text was updated successfully, but these errors were encountered: