Search code examples
c#linqienumerable

LINQ Concatenation with a single extra element


If we want a single IEnumerable<T> representing the concatenation of of two IEnumerable<T>s we can use the LINQ Concat() method.

For example:

int[] a = new int[] { 1, 2, 3 };
int[] b = new int[] { 4, 5 };

foreach (int i in a.Concat(b))
{
    Console.Write(i);
}

of course outputs 12345.

My question is, why is there no overload of Concat() just accepting a single element of type T such that:

int[] a = new int[] { 1, 2, 3 };

foreach (int i in a.Concat(4))
{
    Console.Write(i);
}

would compile and produce the output: 1234?

Googling around the issue throws up a couple of SO questions where the accepted answer suggests that the best approach when looking to achieve this is to simply do a.Concat(new int[] {4}). Which is fine(ish) but a little 'unclean' in my opinion because:

  • Maybe there is a performance hit from declaring a new array (albeit this is presumably going to be negligible pretty much evey time)
  • It just doesn't look as neat, easy to read and natural as a.Concat(4)

Anyone know why such an overload doesn't exist?

Also, assuming my Googling hasn't let me down - there is no such similar LINQ extension method taking a single element of type T.

(I understand it is trivially easy to roll one's own extension method to produce this effect - but doesn't that just make the omission even more odd? I suspect there will be a reason for it's omission but can't imagine what it could be?)

UPDATE:

Acknowledging the couple of votes to close this as opinion based - I should clarify that I am NOT seeking peoples opinions on whether this would be a good addition to LINQ.

More I am seeking to understand the FACTUAL reasons why it is not ALREADY part of LINQ.


Solution

  • A good reason for inclusion (in one form or another) would be for IEnumerables to be more like functional sequence monads.

    But since LINQ did not arrive until .NET 3.0, and is implemented mostly using extension methods, I can imagine that they omitted extension methods working on a single element of T. Still this is pure speculation on my part.

    They did however include generator functions, that are not extension methods. Specifically the following:

    • Enumerable.Empty
    • Enumerable.Repeat
    • Enumerable.Range

    You could use these instead of homebrew extension methods. The two use cases you mentioned, can be solved as:

    int[] a = new int[] { 1, 2, 3 };
    
    var myPrependedEnumerable = Enumerable.Repeat(0, 1).Concat(a);
    var myAppendedEnumerable = a.Concat(Enumerable.Repeat(4, 1));
    

    It might have been nice if an additional overload was included as syntactical sugar.

    Enumerable.FromElement(x); // or a better name (see below).
    

    The absence of an explicit Unit function is curious and interesting

    In the interesting MoreLINQ series of blog posts by Bart De Smet, illustrated using the System.Linq.EnumerableEx, the post More LINQ with System.Interactive – Sequences under construction specifically deals with this question, using the following appropriately named method for constructing a single element IEnumerable.

    public static IEnumerable<TSource> Return<TSource>(TSource value);
    

    This is nothing but the return function (sometimes referred to as unit) used on a monad.

    Also interesting is the blog series by Eric Lippert on monads, which features the following quote in part eight:

    IEnumerable<int> sequence = Enumerable.Repeat<int>(123, 1);
    

    And frankly, that last one is a bit dodgy. I wish there was a static method on Enumerable specifically for making a one-element sequence.

    Furthermore, the F# language provides the seq type:

    Sequences are represented by the seq<'T> type, which is an alias for IEnumerable. Therefore, any .NET Framework type that implements System.IEnumerable can be used as a sequence.

    It provides an explicit unit function as Seq.singleton.

    Concluding

    While none of this provides us with facts that shed light on the reasons why these sequence constructs are not explicitly present in c#, until someone with knowledge of the design decision process shares that information, it does highlight it would be worth knowing more about.