What's the best name for a non-mutating “add” method on an immutable collection?

前端 未结 30 1292
夕颜
夕颜 2020-11-29 16:47

Sorry for the waffly title - if I could come up with a concise title, I wouldn\'t have to ask the question.

Suppose I have an immutable list type. It has an operat

30条回答
  •  臣服心动
    2020-11-29 17:14

    Maybe the confusion stems from the fact that you want two operations in one. Why not separate them? DSL style:

    var list = new ImmutableList("Hello");
    var list2 = list.Copy().With("World!");
    

    Copy would return an intermediate object, that's a mutable copy of the original list. With would return a new immutable list.

    Update:

    But, having an intermediate, mutable collection around is not a good approach. The intermediate object should be contained in the Copy operation:

    var list1 = new ImmutableList("Hello");
    var list2 = list1.Copy(list => list.Add("World!"));
    

    Now, the Copy operation takes a delegate, which receives a mutable list, so that it can control the copy outcome. It can do much more than appending an element, like removing elements or sorting the list. It can also be used in the ImmutableList constructor to assemble the initial list without intermediary immutable lists.

    public ImmutableList Copy(Action> mutate) {
      if (mutate == null) return this;
      var list = new List(this);
      mutate(list);
      return new ImmutableList(list);
    }
    

    Now there's no possibility of misinterpretation by the users, they will naturally fall into the pit of success.

    Yet another update:

    If you still don't like the mutable list mention, even now that it's contained, you can design a specification object, that will specify, or script, how the copy operation will transform its list. The usage will be the same:

    var list1 = new ImmutableList("Hello");
    // rules is a specification object, that takes commands to run in the copied collection
    var list2 = list1.Copy(rules => rules.Append("World!"));
    

    Now you can be creative with the rules names and you can only expose the functionality that you want Copy to support, not the entire capabilities of an IList.

    For the chaining usage, you can create a reasonable constructor (which will not use chaining, of course):

    public ImmutableList(params T[] elements) ...
    
    ...
    
    var list = new ImmutableList("Hello", "immutable", "World");
    

    Or use the same delegate in another constructor:

    var list = new ImmutableList(rules => 
      rules
        .Append("Hello")
        .Append("immutable")
        .Append("World")
    );
    

    This assumes that the rules.Append method returns this.

    This is what it would look like with your latest example:

    var suite = new TestSuite(x => x.Length);
    var otherSuite = suite.Copy(rules => 
      rules
        .Append(x => Int32.Parse(x))
        .Append(x => x.GetHashCode())
    );
    

提交回复
热议问题