Daniel Cazzulino made an interesting post a little while ago that I've been meaning to follow up. Ian Griffiths picked up on one aspect of it (the interesting, obscure type inference issue - and I love the idea of "mumble types") which reminded me to go have another look.
The gist of Daniel's post is to rewrite some Ruby code using some of the nice new C# features. The Ruby code is using Fibers (think iterators) to take a sequence as input, and output a new sequence of different values, or a new sequence made by skipping values.
And I really had to torture that sentence to avoid saying "map" and "filter". Remember, they're the magic words...
Daniel follows the original article very faithfully. He replaces the Fibers with iterators, adds some syntax sugar with extension methods and lambda functions, and duplicates the functionality of LINQ. He just stops short of using the nice from...where...select syntax. So let's have a look at the last few examples.
Get 10 integers that are both even and multiples of three:
var evenMultiplesOfThree = from x in Enumerable.Range(1, 1000)
where (x % 2) == 0 && (x % 3) == 0
select x;
foreach (int i in evenMultiplesOfThree.Take(10))
Console.WriteLine(i);
(OK, slightly different. Daniel's range is infinite, I've capped mine.)
The pipeline for tripling even numbers, adding one and only printing the first 5 multiples of 5 (and this one lets me use "let"):
var results = from x in Enumerable.Range(1, 1000)
let y = (x * 3) + 1
where (y % 5) == 0
select y;
foreach (int i in results.Take(5))
Console.WriteLine(i);
And the (somewhat unwieldy) palindrome finder:
var words = "Madam, the civic radar rotator is not level".Split(' ');
var palindromes = from word in words
let normalised = new {
Original = word,
Normalised = word.ToLower()
}
select "'" + normalised.Original + "' is " +
((normalised.Normalised != new string(normalised.Normalised.Reverse().ToArray()))
? "not " : "") + "a palindrome";
foreach(var palindrome in palindromes)
Console.WriteLine(palindrome);
But this is an awkward query. Perhaps a poor example to convert into a query, and a simple loop might have served better.
Personally, I don't like the idea of having a ForEach extension method. I like doing the explicit foreach command. I think it separates the enumeration over the results from the description of the query (pipeline).
I think Daniel's post validates the design of LINQ. Independently, he took iterators, lambdas and extension methods and built something that, apart from the syntax, is LINQ. That's good design, and shows that LINQ is really evolutionary in the way it's built on so many smaller features in the runtime. But by not going that final step, it also shows that LINQ, and especially the query syntax, is really revolutionary. I think it's quite a mind shift to go from a "pipeline", a processing concept that's very procedural, explicitly procedural, and convert it into a query, something that's much more declarative.