-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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: Add full family of Immutable collection contracts. #20419
Comments
I know that IReadOnlySet currently not exists but I home sometime this proposal will be implemented https://github.com/dotnet/corefx/issues/1973 |
|
|
Why do I need to distinguish between ReadOnly vs. Frozen? |
One improvement: I am writing polyfill library that implements this proposal. And yes I found how we can re-use IImmutable** Stay tuned. |
I have the same question as @karelz, what is the difference between doing this and using |
Unfortunately this deep is question. I still working on a library with new proposed API preview. Code is better that 1000 words. Solution consist from multiple parts:
Current code will be useful wihout dotnet/csharplang#52 but with some amount of contract mangling. |
@dmitriyse we will need a "short" description of the problem your code is trying to solve. Why don't you start with the questions I asked above? Having complex library with new API proposed is unlikely going to help or answer any questions of WHY. Solution is second step. Problem description and agreement it is a problem worth solving needs to be the first step. Quantification of the problem is usually part of the problem discussion. I just want to make sure you don't waste time building API proposal which we won't consider until we understand (and agree with) the problem it is trying to solve. |
I need this features in my project firstly. Secondly I wish that this improvements to become in BCL. It's seem enough general and useful.
|
Please wait another day and I share class diagram. |
I am not sure how class diagram can help explain WHY you need it. It will explain more HOW to solve it. Isn't it that case? ... Anyway, we can wait. Why do you need the guarantee of Immutable? What kind of SW needs it? -- that is the basic question which needs to be thoroughly explained first. |
Finally I established how to combine all ideas together. First commit with very raw preview is published here https://github.com/dmitriyse/ClrCoder/tree/master/src/ClrCoder.Polyfill The problem: public class MyClass
{
public MyClass(IReadOnlyList<string> someIds)
{
// Currently I cannot rely that someIds will never change.
// I every time think: should I copy this collection or I can use it as-is.
// Ok, it's not a trouble I can copy, but stop, what if this place will be performance critical ???
// no I don't want to copy.
// Let assume nobody will never change this collection. <== TOTALLY WRONG!
SomIds = someIds;
// also I performed some loading based on this ids.
// and store result in the instance.
// ids list will be hardly used by this component and consumers of this components.
}
public IReadOnlyList<string> SomeIds {get;}
}
// Some other place, some newbie developer...
public void SomeCreationPlace()
{
// ...
// Some algorithm with bugs.
var ids = new List<string>();
while (someCondition)
{
for(int i = 0; i++; i <myVar)
{
ids.Add("test"+i);
}
// ids always changes but MyClass think that IReadOnlyList will never be changed.
var myComp = new MyClass(ids);
myComponents.Add(myCom);
// Some other actions
// Ids should be cleared here, but WE PUT SOME STUPID BUG HERE
}
} Final bug can be found in totally different place where consumers of consumer of consumer of MyComponent received KeyNotFound exception (for example). According to this logic we can say, no no no, better I will make the copies. Imagine we have deep call chain. Every time we cannot rely on IReadOnly** and make copies. What about IImmutableList ??? - it exactly gives guaranty of immutability. using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ClrCoder.Collections.Immutable.Polyfill
{
using System.Collections.Immutable;
public class test: IImmutableList<int>
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count
{
get
{
throw new NotImplementedException();
}
}
public int this[int index]
{
get
{
throw new NotImplementedException();
}
}
public IImmutableList<int> Clear()
{
throw new NotImplementedException();
}
public IImmutableList<int> Add(int value)
{
throw new NotImplementedException();
}
public IImmutableList<int> Replace(int oldValue, int newValue, IEqualityComparer equalityComparer)
{
throw new NotImplementedException();
}
public IImmutableList<int> SetItem(int index, int value)
{
throw new NotImplementedException();
}
public IImmutableList<int> RemoveAt(int index)
{
throw new NotImplementedException();
}
public IImmutableList<int> RemoveRange(int index, int count)
{
throw new NotImplementedException();
}
public IImmutableList<int> RemoveRange(IEnumerable items, IEqualityComparer equalityComparer)
{
throw new NotImplementedException();
}
public IImmutableList<int> RemoveAll(Predicate match)
{
throw new NotImplementedException();
}
public IImmutableList<int> Remove(int value, IEqualityComparer equalityComparer)
{
throw new NotImplementedException();
}
public IImmutableList<int> InsertRange(int index, IEnumerable items)
{
throw new NotImplementedException();
}
public IImmutableList<int> Insert(int index, int element)
{
throw new NotImplementedException();
}
public IImmutableList<int> AddRange(IEnumerable items)
{
throw new NotImplementedException();
}
public int LastIndexOf(int item, int index, int count, IEqualityComparer equalityComparer)
{
throw new NotImplementedException();
}
public int IndexOf(int item, int index, int count, IEqualityComparer equalityComparer)
{
throw new NotImplementedException();
}
}
} Really too heavy. I do not want Add(), Remove(). I just what IReadOnlyList + Immutability guaranty. CURRENTLY WE HAVE NOTHING. We can introduce slim immutable contracts (with usual hierarchy): public class MyClass
{
// No comments all clear.
public MyClass(IImmutableListSlim<string> someIds)
{
SomIds = someIds;
}
// This collection instance can travel across components, threads, call chains safely.
public IImmutableListSlim<string> SomeIds {get;}
} This is only about why we needs IImutable*** slim collections. Another big question how to inject this features effectively. |
OK, now I understand what you're trying to achieve high level. |
Let's scare a little bit. I added IsImmutable into IEnumerable, sounds crazy. Just because of that like changes we hardly needs implementation of dotnet/csharplang#52 (was dotnet/csharplang#222). public void Test()
{
var mySet = new HashSet<string>(){"Id","abc", "dfdf"};
var a = mySet.ToImmutable(); // Perform clone and returns IImmutableSetSlim<string>
mySet.Freeze(); // mySet.IsImmutable now becomes true. (Actually IEnumerable<T>.IsImmutable)
var b = mySet.ToImmutable(); // Returns light wrapper with IImmutableSetSlim<string> contract.
} |
There multiple ways to solve this problem:
|
Even if it is API level, you can consider solving it on top of BCL, not necessarily in CoreFX (e.g. create |
Enumeration, collections, collection contracts it's whole ecosystem (currently immutability unaware). Possibly it's time to improve, we also have inheritance inconsistency, missing of IReadOnlySet, miss of Comparable property on ISet, IDictionary. Probably something else. So I am trying to design one solution BCL collections 2017+ Of course I can do it on top of BCL, but it will be new collections ecosystem with bridges to old. |
I see that you are very excited and motivated - that is great! However, I would like you to apply caution. Think things through first. Split them into separate isolated problems. Suggest APIs in small batches which logically belong together. Write down justifications and reasons for each API addition, list use cases. Get everyone on board with the direction and plan first. If you mix up all proposals together, then either the issue will not get any traction because it will be hard to grasp. And if it is truly necessary (not just easier) to do it all at once (you will need to convince everyone that's the case), we will likely need another place to have a very solid and lengthy design discussion, before we jump into such endeavor. GitHub issues are bad for lengthy design discussions. |
Thank you for your support. So probably it's a wrong place to discuss improvement ideas and form them as a feature request. Also I found that is possible to compile all CLR/dotnet and try to play with S.P. CoreLib. Only after that we can talk here. |
We can close this issue, I found a way how to build useful nuget packages without BCL modifications (but with some restrictions and workarounds). |
Sounds good. Let us know when you are ready to start the discussion again - we can find the right place for it. |
Motivation:
Final bug can be found in totally different place where consumers of consumer of consumer of MyComponent received KeyNotFound exception (for example).
According to this logic we can say, no no no, better I will make the copies.
Imagine we have deep call chain. Every time we cannot rely on IReadOnly** and make copies.
Again it's no so C# way to write stupid boilerplate.
What about IImmutableList ??? - it exactly gives guaranty of immutability.
Ok. but now try to implement custom component that implement it:
Really too heavy. I do not want Add(), Remove(). I just what IReadOnlyList + Immutability guaranty.
CURRENTLY WE HAVE NOTHING.
We can introduce slim immutable contracts (with usual hierarchy):
The text was updated successfully, but these errors were encountered: