Concat in MoreLinq

44 views
Skip to first unread message

Jon Skeet

unread,
Sep 11, 2009, 2:35:27 AM9/11/09
to moreli...@googlegroups.com
I've just been answering this SO question:

where MoreLinq was the obvious answer. I'm not sure I like what we've done with Concat though, in two ways:
  • If a sequence implements IEnumerable<Itself> (like a TreeNode class might) then sequence.Concat(item) matches both our method and the BCL one. Admittedly the results will be the same, but...
  • item.Concat(sequence) is cute, but I think it's a step too far. I'm not terribly keen on adding extension methods to all types. (I know ThrowIfNull does so to all classes, but that feels more general purpose - and more useful.)
I wonder whether we should keep the Prepend we've got, and add Append as well.

Alternatively WithHead and WithTail could work:

    sequence.WithHead(headItem)
    sequence.WithTail(tailItem)

Thoughts?

Jon

Atif Aziz

unread,
Sep 11, 2009, 4:21:15 AM9/11/09
to moreli...@googlegroups.com
We already have WithHead and it's called Prepend. I think we meant to take out the Concat version that does the same in favor of Prepend but perhaps forgot to do so. With Prepend and Concat, I think we cover the ground well enough and names also work out well within the realm of single-wording.
- Atif

Jon Skeet

unread,
Sep 11, 2009, 4:35:23 AM9/11/09
to moreli...@googlegroups.com
2009/9/11 Atif Aziz <aziz...@gmail.com>

We already have WithHead and it's called Prepend.

Indeed - I was wondering whether WithHead might be clearer. Maybe not.
 
I think we meant to take out the Concat version that does the same in favor of Prepend but perhaps forgot to do so. With Prepend and Concat, I think we cover the ground well enough and names also work out well within the realm of single-wording.

The trouble is the potential ambiguity of Concat with the LINQ to Objects form. If we've got Prepend, wouldn't Append make sense for the reverse?

Jon

Atif Aziz

unread,
Sep 11, 2009, 4:58:09 AM9/11/09
to moreli...@googlegroups.com
Yes, ideally, it would be called Append, but since Concat is already there in LINQ, it would makes single-item overload more naturally discoverable. I would be more inclined to use the name ConcatOne if it is important to have the implementation detail in the face. In real practice, I've never been confused because your variable naming will naturally remove any confusion. For example, is there any question about what's going on here?
 
rows.Concat(row)
 
So if you have sensible variable names, the plurality and singularity will help with semantics.
 
I've used both, Concat and Prepend, in practice and belive it or not, I've had more issues with Prepend because the code ends up reading backwards. Case in point: 
 
columns.Prepend("Time").Prepend("Date");
 
Here, I'm trying to add date and time to an existing set of columns. If I want date to appear before time, it has to be prepended last. Of course, the sensible way to write this would be to use Concat:
 
new[] { "Date", Time" }.Concat(columns);
 
Unfortunately, if you're in the middle of a long chaining of operators, this is not always possible. I can expand further if you like.
 
- Atif

Jon Skeet

unread,
Sep 11, 2009, 5:04:34 AM9/11/09
to moreli...@googlegroups.com
2009/9/11 Atif Aziz <aziz...@gmail.com>

Yes, ideally, it would be called Append, but since Concat is already there in LINQ, it would makes single-item overload more naturally discoverable. I would be more inclined to use the name ConcatOne if it is important to have the implementation detail in the face. In real practice, I've never been confused because your variable naming will naturally remove any confusion. For example, is there any question about what's going on here?
 
rows.Concat(row)

Probably not. But now try this:

public class TreeNode : IEnumerable<TreeNode>

someNodes.Concat(otherNode);
someNodes.Concat(otherNodes);

What will they do? Both methods will be applicable for the first call, which is my concern. I don't like overloads which rely on specificity, basically. In particular, just changing a using directive could completely change the behaviour, which is not usually a pleasant experience :(

I quite like ConcatOne. Or there's always "And"

rows.And(row)

:)

(Not a serious suggestion - it doesn't fit in with the rest of LINQ)
 
So if you have sensible variable names, the plurality and singularity will help with semantics.
 
I've used both, Concat and Prepend, in practice and belive it or not, I've had more issues with Prepend because the code ends up reading backwards. Case in point: 
 
columns.Prepend("Time").Prepend("Date");
 
Here, I'm trying to add date and time to an existing set of columns. If I want date to appear before time, it has to be prepended last. Of course, the sensible way to write this would be to use Concat:
 
new[] { "Date", Time" }.Concat(columns);
 
Unfortunately, if you're in the middle of a long chaining of operators, this is not always possible. I can expand further if you like.

No, I take your point... and that's presumably why we've got the extension method on Concat "in reverse" as well. 

Needs more thought.

Jon

Atif Aziz

unread,
Sep 11, 2009, 5:23:00 AM9/11/09
to moreli...@googlegroups.com
I'd say the TreeNode design is flawed. ;) Just because a TreeNode can contain other tree nodes doesn't mean it has to lift them onto itself. In other words, I'm happy that XNode or XContainer from System.Xml.Linq doesn't do this. Ideally, TreeNode has ChildNodes or Children as a property that returns the sequence of child nodes. Then, Concat is not an issue.
 
In particular, just changing a using directive could completely change the behaviour, which is not usually a pleasant experience :(
 
Yep, but that's a fact of life with extension methods. I wish VS or R# had a way to color those differently.
 
- Atif
Reply all
Reply to author
Forward
0 new messages