A Limitation of Lambda Expressions and Overloaded Extension Methods

Picture by Roger Ferrer Ibáñez, Some Rights Reserved (CC-NC-SA)Tamir hates lambdas. He was having a problem with one of his lambda expressions and twittered about it. Around that time I opened my twitter account (yes, Yosi finally convinced me) and offered my help.

He wanted to have a single extension method that could iterate over a collection and either change or keep the values it got. Kind of like this:

items.ForEach(item => item.SubItems.ForEach(subItem => subItem = newValue));

Where ForEach was defined as:

static void ForEach<T>(this IList<T> collection, Action<T> action)
{
    for (int i = 0; i < collection.Count; i++)
    {
        action(collection[i]);
    }
}

This did not work simply because subItem is a local variable – a copy of either the reference (when using a Reference Type) or of the value itself (when using a Value Type) – the lambda expression was (very loosely) translated to the following:

void Foo(SubItem subItem)
{
subItem = newValue;
}

What I then tried to do was to replace Action<T> with a delegate with a ref parameter, so that the local variable will change. It turns out lambda expressions can not contain ref parameters.

On to the next solution – overloading the ForEach extension method:

static void ForEach<T>(this IList<T> collection, Action<T> action)
{
for (int i = 0; i < collection.Count; i++)
{
action(collection[i]);
}
}
static void ForEach<T>(this IList<T> collection, Func<T, T> action)
{
for (int i = 0; i < collection.Count; i++)
{
collection[i] = action(collection[i]);
}
}

This way, one can now write the following:

items.ForEach(item => item.SubItems.ForEach(subItem => newValue));

The first call (outer ForEach) will use the overload with Action<T> and the second (inner ForEach) will use the Func<T, T> overload and end up changing the current subItem. The C# compiler does a nice job finding the best overload to call.

Advertisements

6 thoughts on “A Limitation of Lambda Expressions and Overloaded Extension Methods

  1. I think that lambdas not being able to contain ref parameters is a good decision. They’re supposed to be these simple, side-effect free functions, and anything with ref is neither of the above.
    If you insist on using lambdas and extensions for this scenario, I think it might be better to use some kind of Map method that returns a new collection instead of editing the old one (and this will also work for collections that only implement IEnumerable and not IList).

  2. Doron – I see no reason to limit the power of lambdas in comparison to anonymous delegates and even methods.
    I agree about the IEnumerable comment, but the original method required IList in order to be able to iterate by index.
    Aaron – I have yet to dabble in VB.NET, sorry :)

  3. Why do you guys use
    for (int i = 0; i < collection.Count; i++)
    instead of
    foreach( item in collection)
    ??

  4. Complete misunderstanding of functional programming. Side-effect free is the name of the game.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s