2

It certainly does not break from the standard practice of the .NET framework. When I see a a + b I always assume something new will be created.

static void Main(string[] args)
{
    var list = BuildList(ImmutableList<int>.Empty);
    var sum = (list + 500).Sum();
    Console.WriteLine(sum);
    Console.ReadLine();
}

static ImmutableList<int> BuildList(ImmutableList<int> list)
{
    if (list.Count < 1000)
    {
        return BuildList(list + list.Count);
    }
    return list;
}

Update

See Jon Skeet's post on what to name methods on immutable lists.

Surprising Responses

I am quite surprised to see so many answers that concur that this makes sense. In principle I also agree but it is way easier for me to read verbose code than it is for someone who is uncomfortable with terseness to read, well terse code. From my working experience they seem to be the majority.

Community
  • 1
  • 1
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157

5 Answers5

6

I personally would not recommend overloading operators like + for any class that isn't meant to be treated as a primitive. In particular, collections, in my opinion, should never overload operators.

Operator overloading makes sense when you have a small immutable class or struct that is meant to behave like a "primitive" type. However, when you start trying to do this for other classes, it really impacts maintainability.

I'd recommend making explicit method calls, instead.


After reading the comments, my recommendation would be to use Concat() (to match Enumerable.Concat in the Framework). Another option I would prefer would be Construct(), ala cons (though cons typically prepends), to make the usage very clear:

var list = BuildList(ImmutableList<int>.Empty);
var list2 = list.Concat(500); 
// Or: var list2 = list.Construct(500);
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • This is a `struct` that cannot be mutated in any way, so in essence it does behave exactly as an `int` would. – ChaosPandion Aug 07 '10 at 00:40
  • @ChaosPandion: No, it really doesn't. It's immutable like an int, but it's conceptually very different. A collection is conceptually not a primitive object. – Reed Copsey Aug 07 '10 at 00:42
  • 1
    @ChaosPandion: I'm not saying you ~can't~ do this - just that I feel it's adding something very unclear to your API, and likely to reduce the maintainability and understandability of your code dramatically. – Reed Copsey Aug 07 '10 at 00:42
  • @Reed - So lets say you could define any *punctuation* as an operator would you think something like `list :: 2` would be appropriate? – ChaosPandion Aug 07 '10 at 00:47
  • 1
    +1. Reed's opinion matches Microsoft's Framework Design Guidelines. – TrueWill Aug 07 '10 at 00:47
  • @ChaosPandion: I wouldn't do it. I'd much rather use method names. What the hell is "list :: 2" supposed to mean? If I was reading that code, I'd be scratching my head.... – Reed Copsey Aug 07 '10 at 00:48
  • (BTW - this is just my opinion, and comes from having to debug C++ with lots of operator overloads too many times.... Life gets very hard, very quickly when people start overloading things in non-obvious ways) – Reed Copsey Aug 07 '10 at 00:49
  • @ChaosPandion: I'd personally use F# or Haskell when you want it - but don't pervert C# into looking more like F#. People have done that with C++, and it really goes awry :( – Reed Copsey Aug 07 '10 at 00:51
  • @Reed - What are your thoughts on a proper name for the "add" method? – ChaosPandion Aug 07 '10 at 00:53
  • 1
    @ChaosPandion: What's this method going to return? if you do `list = new IL([] {1,2,3}); list2 = list.Foo(500);`, what would list2 contain? – Reed Copsey Aug 07 '10 at 00:56
  • @ChaosPandion: I'd probably use Concat, as it's behavior is really identical to Linq's concat (though for 1 item instead of a collection): http://msdn.microsoft.com/en-us/library/bb302894.aspx - If you don't like that, another good option would be Construct() – Reed Copsey Aug 07 '10 at 00:59
  • @Reed - Thanks, just remember some people ask questions simply out of curiosity. :) – ChaosPandion Aug 07 '10 at 01:05
  • @ChaosPandion: Of course! I'm just trying to help - sorry if it came across preachy ;) This is one where I feel fairly strongly (I inherited a huge project a few years back full of C++ code with lots of bugs from overloaded operators - made life very difficult for a good year :( ) – Reed Copsey Aug 07 '10 at 01:15
  • I would think the best thing in many cases would be to use a static method to combine the two immutable lists, rather than an instance member on the immutable-list type. Not only would it be easier to recognize that such a thing leaves the original list untouched, but if `ImmutableList` were covariant on `T`, one could do `var CatsAndDogs = ImmutableList.Concat(ListOfCats, ListOfDogs);` Incidentally, I would think that if Microsoft had individual delegate types override `Combine`, similar semantics could have been used there. – supercat Aug 21 '12 at 19:16
5

An immutable list of characters that overloads + makes sense; we call them strings. You could, by analogy, say that an immutable list of anything that overloads the + operator to invoke the Concat operation makes sense.

BTW, I recently created a similar function to what you're describing:

public static IEnumerable<T> Append<T>(this IEnumerable<T> list, T item)
{
    foreach (T i in list)
        yield return i;
    yield return item;
}

I decided that Append adds a single element to the list, while Concat adds a whole list to the end of the list.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • 1
    +1 - You know this is what I was thinking, but the confusion this seems to cause people really outweighs the conciseness of using it. Now I were to never use this outside of my personal projects then I would probably use it, but I want to use this at my job as well. *(I am so sick of the issues cause by nullable mutable lists.)* – ChaosPandion Aug 07 '10 at 01:49
  • Maybe I'm not confused because Python uses `+` for list concatenation, but I remember not being confused when I first saw it. What kind of people are confused by `list + list`? – Gabe Aug 07 '10 at 02:01
  • 1
    because some people don't *think*. – Joshua Aug 07 '10 at 02:03
  • That is perfect. I have my interface. :) – ChaosPandion Aug 07 '10 at 14:12
2

What does + mean in this context? It seems really unclear to me (without reading code). IList + IList I could see as maybe a union operator or combining the lists (keeping duplicates) or any number of other things. IList + int just confuses me. It seems to me a method with a clear name will make for much more readable code than an operator somebody will have to look up, or comments every time you use the overloaded operator.

PeterL
  • 1,397
  • 11
  • 15
  • What would you name this method? – ChaosPandion Aug 07 '10 at 00:51
  • Assuming this adds an element to the list, the link you provided to Jon Skeet's post is a good resource. I personally would probably throw my hat in as a vote for `.Cons()` or `.Add()` -- aside from mutability, this does the same as `.Add()` on a `List`. – PeterL Aug 07 '10 at 01:05
  • I am on the fence between `Cons` and `Concat`. – ChaosPandion Aug 07 '10 at 01:07
  • The cons operation puts a new element on the head of a list; the concat operation creates a new list containing all of the elements of the first list, then all of the elements of the second list. They are fundamentally different operations. – Gabe Aug 07 '10 at 01:29
2

One thing about operators, is that people expect them to be relatively efficient. There's nothing to say that this should hold, but people do seem to expect it.

If internally, the structure of such a list allowed you to create a concatenated list by having it store references to the two it was sourced from (quite possible, especially considering that immutability negates the risk of confusing side-effects), then I'd go for it. If it was potentially to have to do a relatively expensive operation, then I wouldn't. "concatenate()" might make someone consider "do I really need to move this much memory around here in this tight loop I'm currently in, or should I consider a different approach" at a time when such a thought is appropriate.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • I ran some performance tests and my implementation does surprisingly well with results similar to `string`. Obviously not as good but similar. – ChaosPandion Aug 07 '10 at 01:55
  • Though, "know that + is slow with strings" is often one of the first things told to newbies, precisely because they may assume otherwise (and then it gets overstated to the point of over avoiding it, but that's a different matter). At that speed, I think I'd lean against, but I wouldn't think it wrong of someone to do either. – Jon Hanna Aug 07 '10 at 11:33
  • In the cases where you need a tight loop. I would say use a mutable list but keep it scoped to a single function. – ChaosPandion Aug 07 '10 at 14:13
  • Agreed, but remember that ultimately there is no code difference between + and Concat, the difference is only in the mind of the human being reading and writing the code. The effect is hence to encourage or discourage different approaches; it's a psychological thing about what the person does in a tight loop, rather than the practical thing of what they should do, and we want the psychology and the practical concerns to match. – Jon Hanna Aug 07 '10 at 14:52
1

I'd do it.

After all, a string is a list of characters.

And yes, if I saw <list> + <list> I'd immediately think concatenate (the only other possibility is vector sum and that's rare).

Joshua
  • 40,822
  • 8
  • 72
  • 132