I have an array of ordered enumerables IorderedEnumerable
and I want to flatten it so that the ordered enumerables of foo
are concat
All LINQ to Objects methods (except, obviously, OrderBy()
and ToDictionary()
) will preserve source ordering.
Lists (represented by IEnumerable<T>
in .net) along with two operations form a monad, which must obay the monad laws. These two operations are given different names in different languages, the wikipedia article uses Haskell which calls them return
and >>=
(called 'bind'). C# calls >>=
SelectMany
and does not have a built-in function for return
. The names are unimportant however and what matters is the types. Specialised for IEnumerable<T>
these are:
Return :: T -> IEnumerable<T>
SelectMany :: IEnumerable<T> -> Func<T, IEnumerable<U>> -> IEnumerable<U>
Return
simply returns a 1-element sequence containing the given element e.g.
public static IEnumerable<T> Return<T>(T item)
{
return new[] { item };
}
SelectMany
is already implemented as Enumerable.SelectMany
:
public static IEnumerable<U> SelectMany<T, U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> f) { ... }
SelectMany
takes an input sequence and a function which generates another sequence for each item of the input sequence and flattens the resulting sequence of sequences into one.
Restating the first two monad laws in C# we have:
Left identity
Func<T, IEnumerable<U>> f = ...
Return(x).SelectMany(f) == f(x)
Right identity
IEnumerable<T> seq = ...
seq.SelectMany(Return) == seq
By the right identity law, SelectMany
must flatten each sequence generated by the Func<T, IEnumerable<U>>
according to the order of the input elements.
Assume it flattened them in reverse order e.g.
new[] { 1, 2 }.SelectMany(i => new[] { i, -i }) == new[] { 2, -2, 1, -1 }
then
var s = new[] { 1, 2 }
s.SelectMany(Return) == new[] { 2, 1 } != s
which would not satisfy the right-identity law required.