Welcome to Atalasoft Community Sign in | Help

Being Lazy

Here’s a chunk of useless code to demonstrate a nifty feature of the Select extension method:

static char LowerCase(char c)
{
    // put a breakpoint here
    Console.WriteLine("Lowering " + c);
    return Char.ToLower(c);
}
static IEnumerable LowerCaseEnumerator(string s)
{
    return s.ToCharArray().Select(c => LowerCase(c));
}
static void Main(string[] a)
{
    var p = LowerCaseEnumerator("ABCdEF");
    // put second breakpoint here
    IEnumerator en = p.GetEnumerator();
    while (en.MoveNext()) {
        Console.WriteLine(en.Current);
    }
}

This is excessive code to create a lower-casing enumerator for a string.  Clearly useless.  The interesting part is the breakpoints.  If you set break points as directed, which breakpoint will get hit first?  The one in LowerCase or the one in Main?

If you answered LowerCase, read on.  Main-iacs?  You can step aside.  The breakpoint in Main gets hit first.  Select constructs a class that can enumerate the object in question, but it doesn’t call the lambda expression until the actual enumeration happens.  This is great – the enumeration is lazy, but it did make me raise my eyebrow the first time I started playing with it.  It makes sense, though.  Why do the work of calling the lambda if it never gets used or not across every element.  And while it’s expensive and clearly an abuse of Select, I can use it as a mechanism to defer printing:

var p = "now I will print".ToCharArray().Select(c => { Console.Write(c); return c; });
Console.Write("Deferring: ");
p.ToArray();

This code outputs “Deferring: now I will print”.  There is a better way to do this, though.  If I remember my CS right, a closure is a type of lambda expression that also contains variables that are bound to particular values at creation time.  Thus, I can create a specialized deferred printer that doesn’t feel like an abuse of Select:

static Func<bool> GetDeferredPrinter(string s)
{
    return () => { Console.Write(s); return true; };
}
static void Main(string[] a)
{
    Func<bool> defer = GetDeferredPrinter("now I will print.");
    Console.Write("Deferring: ");
    defer();
}

This does the same thing as before.  The downside is that I’m using the Func<T> and there is no way to say Func<void> for functions that have no return value.

There is a problem though – if you bind values into your closure which are NOT invariant, you may get rather unexpected behavior.  For example,  consider this extension to our current deferred printer:

 

class StringHolder
{
    private string _s;
    public StringHolder(string s)
    {
        _s = s;
    }
    public String String { get { return _s; } set { _s = value; } }
}
static void Main(string[] a)
{
    StringHolder holder = new StringHolder("now I will print.");
    Func<bool> defer = GetDeferredPrinter(holder);
    Console.Write("Deferring: ");
    defer();
    holder.String = " warming up.";
    defer();
}

I’ve added a new class StringHolder which contains a set/get property String.  If I call defer twice and alter the String property, it will print something new.  This is not unexpected, but you should always keep it in your mind if you’re trying to create closures to activate later.

Summary: Select uses lazy evaluation of the lambda expression – a tool for good and evil.  We can use this ability to make closures that retain data and state for later use.

Published Wednesday, December 17, 2008 1:57 PM by Steve Hawley
Filed under:

Comments

No Comments
Anonymous comments are disabled