Reinventing LINQ

by matt 21. March 2008 00:54

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.

Tags:

Comments

Add comment


(Will show your Gravatar icon)

biuquote
  • Comment
  • Preview
Loading



About the author

Something about the author

Calendar

<<July 2010>>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

RecentComments

Comment RSS

License

Creative Commons License
Except where otherwise noted, content on this site is by Matt Ellis and is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

©2010 Matt Ellis