232
votes

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 operation Foo(x) which returns a new immutable list with the specified argument as an extra element at the end. So to build up a list of strings with values "Hello", "immutable", "world" you could write:

var empty = new ImmutableList<string>(); var list1 = empty.Foo("Hello"); var list2 = list1.Foo("immutable"); var list3 = list2.Foo("word"); 

(This is C# code, and I'm most interested in a C# suggestion if you feel the language is important. It's not fundamentally a language question, but the idioms of the language may be important.)

The important thing is that the existing lists are not altered by Foo - so empty.Count would still return 0.

Another (more idiomatic) way of getting to the end result would be:

var list = new ImmutableList<string>().Foo("Hello") .Foo("immutable") .Foo("word"); 

My question is: what's the best name for Foo?

EDIT 3: As I reveal later on, the name of the type might not actually be ImmutableList<T>, which makes the position clear. Imagine instead that it's TestSuite and that it's immutable because the whole of the framework it's a part of is immutable...

(End of edit 3)

Options I've come up with so far:

  • Add: common in .NET, but implies mutation of the original list
  • Cons: I believe this is the normal name in functional languages, but meaningless to those without experience in such languages
  • Plus: my favourite so far, it doesn't imply mutation to me. Apparently this is also used in Haskell but with slightly different expectations (a Haskell programmer might expect it to add two lists together rather than adding a single value to the other list).
  • With: consistent with some other immutable conventions, but doesn't have quite the same "additionness" to it IMO.
  • And: not very descriptive.
  • Operator overload for + : I really don't like this much; I generally think operators should only be applied to lower level types. I'm willing to be persuaded though!

The criteria I'm using for choosing are:

  • Gives the correct impression of the result of the method call (i.e. that it's the original list with an extra element)
  • Makes it as clear as possible that it doesn't mutate the existing list
  • Sounds reasonable when chained together as in the second example above

Please ask for more details if I'm not making myself clear enough...

EDIT 1: Here's my reasoning for preferring Plus to Add. Consider these two lines of code:

list.Add(foo); list.Plus(foo); 

In my view (and this is a personal thing) the latter is clearly buggy - it's like writing "x + 5;" as a statement on its own. The first line looks like it's okay, until you remember that it's immutable. In fact, the way that the plus operator on its own doesn't mutate its operands is another reason why Plus is my favourite. Without the slight ickiness of operator overloading, it still gives the same connotations, which include (for me) not mutating the operands (or method target in this case).

EDIT 2: Reasons for not liking Add.

Various answers are effectively: "Go with Add. That's what DateTime does, and String has Replace methods etc which don't make the immutability obvious." I agree - there's precedence here. However, I've seen plenty of people call DateTime.Add or String.Replace and expect mutation. There are loads of newsgroup questions (and probably SO ones if I dig around) which are answered by "You're ignoring the return value of String.Replace; strings are immutable, a new string gets returned."

Now, I should reveal a subtlety to the question - the type might not actually be an immutable list, but a different immutable type. In particular, I'm working on a benchmarking framework where you add tests to a suite, and that creates a new suite. It might be obvious that:

var list = new ImmutableList<string>(); list.Add("foo"); 

isn't going to accomplish anything, but it becomes a lot murkier when you change it to:

var suite = new TestSuite<string, int>(); suite.Add(x => x.Length); 

That looks like it should be okay. Whereas this, to me, makes the mistake clearer:

var suite = new TestSuite<string, int>(); suite.Plus(x => x.Length); 

That's just begging to be:

var suite = new TestSuite<string, int>().Plus(x => x.Length); 

Ideally, I would like my users not to have to be told that the test suite is immutable. I want them to fall into the pit of success. This may not be possible, but I'd like to try.

I apologise for over-simplifying the original question by talking only about an immutable list type. Not all collections are quite as self-descriptive as ImmutableList<T> :)

49
  • 2
    @Adam: No, the latter is clearly buggy. They're both actually buggy (as they're doing nothing with the result) - but the first doesn't look buggy to me. Commented Feb 6, 2009 at 20:35
  • 36
    Is an uncat a dog? Commented Feb 6, 2009 at 21:15
  • 7
    Concat/Condog... works for me! ;) Commented Feb 6, 2009 at 21:16
  • 11
    Uncat - that'd be a zombie cat. Commented Feb 7, 2009 at 0:22
  • 2
    @Trap: That sucks in terms of making the API fluid though. Commented Mar 9, 2009 at 15:02

76 Answers 76

1 2
3
0
votes

How about creating a wrapper class with an Augment (or AugmentWith) method?

Sign up to request clarification or add additional context in comments.

1 Comment

Maybe... not terribly convinced.
0
votes

Since this question is now basically a thesaurus: How about .Bring(). As in, give me this list and bring this element with it?

Foo = List.Bring('short'); .Bring('longer'); .Bring('woah'); 

It doesn't roll off the tongue, but it means it, to me.

Actually, AndBring() might be even better.

2 Comments

I can't say I'm a big fan - I don't think I'd really know what it was meant to do without looking it up. Keep 'em coming though :)
huh, I've liked this one... for first 15 seconds)
0
votes

I'd go with operator overloading +. The reason is that that's the way it works in Python - .append() on a list mutates the list, while doing + with another list creates a new list. + also definitely does not imply mutation. I think that's why you like .Plus() so much, so if you don't want to operator overload, then you can go with .Plus().

Comments

0
votes

list.copyAndAppend(elt)

How does that sound?)

Comments

0
votes

As was previously mentioned, you're trying to do 2 things at once and micro-optimizing just for that purpose. If copying occurs away from original collection definition, names like "Add","Append" will only confuse.

"CloneAppend" is what I might use in this situation. Something tells me I wouldn't be in this situation. I believe that soon you'll find yourself in need of other kinds of similar operations like "CloneFirstN" or "CloneRemoveLast". And soon you'll realize that it's much better to chain the clone/copy method, append/remove whatever you need, then convert the collection back to immutable even if it takes an extra line of code.

1 Comment

@Andrei: No, I'm afraid I disagree. Switching between mutable and immutable states ends up being much harder to understand. Operations like this are a core part of functional programming - the idea of "create a new list which has the contents of the old list but with this new item as well" isn't really "two things at once" IMO.
0
votes

Personally I would call the method Clone and call the parameter AdditionalValue as that is essentially what it appears to be doing and would be easily understandable e.g.

var empty = new ImmutableList<string>(); var list1 = empty.Clone("Hello"); var list2 = list1.Clone("immutable"); var list3 = list2.Clone("word"); 

2 Comments

Passing an argument to a clone method looks pretty odd to me.
Better to name it CloneWith in that case.
0
votes

For this kind of functions I usually use the verb at the second form, Added in this case.

This convention is used by Qt, for example QVector2D::normalized returns a normalized vector, while QVector2D::normalize normalizes a vector.

In the same way Added would return the object with a new added item.

1 Comment

I can see the verb working when there are no parameters, but using Added(extraTest) sounds odd to me. But this question shows just how subjective it all is :)
0
votes

I would go with "CreateNewListWithAdditionalItems"

I dont mind long names, but this one is telling you what it will actually do. Also I would throw an exception if you use Add or any other altering methods.

Comments

0
votes

Borrowing from C, how about Concat

Comments

0
votes

The problem with methods like String.Replace is that the user can mistakenly think that there's mutation going on because he can ignore the return value. So use an out parameter for the "return" value. The user can't ignore that:

public class ImmutableList<T> { public ImmutableList(params T[] items) { // ... } /// <summary> /// Creates a new list with the items in this list plus the supplied items. /// </summary> /// <param name="newList"> /// The new list created for the operation. /// This is also the return value of this method. /// </param> /// <param name="items">Items to add to the new list.</param> /// <returns>The new list.</returns> public ImmutableList<T> Add(out ImmutableList<T> newList, params T[] items) { // ... } } 

Usage:

var list = new ImmutableList<string>("Hello", "Immutable"); ImmutableList<string> newList; list.Add(out newList, "World"); 

4 Comments

Unfortunately that makes it a complete pain to use in a fluent way :(
I agree, that's why I've added the params parameter as a mitigation strategy for the "chaining" usage. But that only works if you're "chaining" Adds :-(
You can also return the newList as well, ala strcpy.
I think the ref approach is sometimes under-appreciated. While it is often useful to have a function-style alternative to allow for cases where one may not wish to modify the storage location which holds the original reference, I think the ref approach is clearer in cases where the same storage location will be source and destination.
0
votes

Personally I'd go with Pad(..) perhaps if you're after a fluent style, also with an extension method of AndWith(..)

var list = new ImmutableList<string>().Pad("Hello") .AndWith("immutable") .AndWith("word"); 

Comments

0
votes

I know this is beating a seemingly dead horse, but I think you may be approaching this in an odd way (hence the naming difficulty). When I have an immutable object, I don't often expect that the object will have functions that imply mutability (like Add or AddAll). I know that String & DateTime do this, but even I have fallen victim to forgetting to use the results from DateTime / String functions so I would avoid those pitfalls if possible.

If I were you, I would follow the Builder pattern similar to what Guava uses. See immutable collections in Guava. Basically the concept is that you construct the immutable object with everything it is ever going to contain. If you want to make a new immutable object, you need to use a new builder.

var suite = new TestSuite.Builder<string, int>().add(newTest).build(); 

You can expand upon the builder to allow a variety of modifications to the test suite (using standard list naming conventions like add / remove if you wish) before the user calls build.

Comments

0
votes

A very late answer, but I wanted to add my two cents: I think the point here could be the verb tense more than the verb itself:

var output= myList.joinedWith("Element"); //or AddedTo, ConcatenatedTo... 

The participle changes the meaning, in my opinion: we are no adding anything to myList, but returning the result of the operation.

Comments

0
votes

Another point in favor of Plus is the new mostly immutable Java time API, where plus() is for example used to add to Instants.

Comments

0
votes

Instead/In addition to a name, immutable containers collections are a classic case for operator overloading (as is already the case for int/string/etc). There are two advantages here:

  • conciseness: l1 = l + o1 + o2 rather than l1 = l.Add(o1).Add(o2)

  • using assignment operators: l += o rather than l = l.Add(o)

Using operators also makes the code less error prone, it is common when switching from mutable to immutable types to forget the assignment and just write ie l.Add(o); (with disastrous effects) however just writing l+o; without using the result really sticks out.

You can see this in action in the collections that come as part of the F package.

Comments

0
votes

Consistency in those things is important, I like to follow the naming conventions in Swift API Design Guidelines:

  • Name functions and methods according to their side-effects
    • Those without side-effects should read as noun phrases, e.g. x.distance(to: y), i.successor().

    • Those with side-effects should read as imperative verb phrases, e.g., print(x), x.sort(), x.append(y).

    • Name mutating/non-mutating method pairs consistently. A mutating method will often have a non-mutating variant with similar semantics, but that returns a new value rather than updating an instance in-place.

      • When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its non-mutating counterpart.

        Mutating Non-mutating
        x.sort() z = x.sorted()
        x.append(y) z = x.appending(y)
      • When the operation is naturally described by a noun, use the noun for the non-mutating method and apply the “form” prefix to name its mutating counterpart.

        Non-mutating Mutating
        x = y.union(z) y.formUnion(z)
        j = c.successor(i) c.formSuccessor(&i)

Since the mutating version of the method is Add, the non-mutating version could be Added or Adding:

// Note that following the convention results in code being written as english sentences var empty = new ImmutableList<string>(); var list1 = empty.Adding("Hello"); var list2 = list1.Adding("immutable"); // `list2` is equal to `list1` adding "immutable" var list3 = list2.Adding("word"); 

Comments

1 2
3

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.