Mathias Brandewinder on .NET, F#, VSTO and Excel development, and quantitative analysis / machine learning.
by Mathias 23. January 2010 00:21

One of the immediate benefits I saw in digging into F# is that it gave me a much better understanding of LINQ and lambdas in C#. Until recently, my usage of LINQ was largely limited to returning IEnumerable instead of List<T> and writing simpler queries, but I have avoided the more “esoteric” features. I realize that now that F# is becoming familiar to my brain, whenever I see a statement in C# which contains a foreach:

foreach (var item in items)
{
   // do something with item.
}

… I ask myself if this could be re-written in a more functional way. Sometimes it works, sometimes not. Just like classic OO Design Patterns, functional programming has its own patterns, and I find that having a larger toolkit of patterns in the back of my mind helps criticizing my own code and think about alternatives and possible improvements.

I encountered one such case a few days ago, with the following snippet:

public bool IsValid()
{
    foreach (var rule in this.rules)
    {
        if (!rule.IsSatisfied())
        {
            return false;
        }
    }

    return true;
}

There is nothing really wrong with this code. However, seeing the foreach statement, and an if statement with a return and no else branch made me wonder how I would have done this in F# – and my immediate thought was “I’d use a Fold”.

The fold exists in LINQ; it’s called Aggregate – one of these intimidating LINQ signatures:

Aggregate(TSource)(IEnumerable(TSource), Func(TSource, TSource, TSource))

Aggregate(TSource, TAccumulate)(IEnumerable(TSource), TAccumulate, Func(TAccumulate, TSource, TAccumulate))

Aggregate(TSource, TAccumulate, TResult)(IEnumerable(TSource), TAccumulate, Func(TAccumulate, TSource, TAccumulate), Func(TAccumulate, TResult))

Practically, what this means is “I have this collection of things, which I want to summarize into one single value – take the list, apply this method to each element, accumulate it and return it”. The prototypical example is the product of a list of numbers. This is how the same function could look like, using “classic” foreach syntax, and using LINQ Aggregate:

public int Foreach(IEnumerable<int> integers)
{
    var runningProduct = 1;
    foreach (var integer in integers)
    {
        runningProduct = runningProduct * integer;
    }
    return runningProduct;
}

public int Aggregate(IEnumerable<int> integers)
{
    var seed = 1;
    return integers.Aggregate(
        seed, 
        (runningProduct, integer) => runningProduct * integer);
}

Note that we need to provide a seed value of 1 to the running product: if we didn’t define that starting value, the product would begin at zero, and stay at zero forever.

For illustration, here is how this would look like in F# – nice, short and to the point:

let product integers = 
  integers |> List.fold (*) 1;;

Applying the same idea to my original rule validation example, this is the LINQ version:

public bool IsValid()
{
    return this.rules.Aggregate(true, (x, rule) => x && rule.IsSatisfied());
}

3 months ago, I’d have kept the original version in place. Today, I ended up picking the LINQ version. I can’t quite say why – it has to do in part with the LINQ terseness, and in part with its focus on what the function intent is, rather than how it does it. On the other hand, I wonder if I would make the same decision if this was not my own code: this is very maintainable, provided that you are comfortable with LINQ, which is a big caveat.

Which one would you pick?

Comments

4/27/2010 6:04:48 AM #

Arvind

Very interesting post! I would personally go for the functional approach, simply because of it's terseness. As far as readability/maintainability is concerned, I would still say that the iterative approach is better, simply because you cannot expect other developers to have the same mastery of the relatively less-known approach as you do.

Arvind United States | Reply

4/28/2010 4:18:48 AM #

Mathias

Hi Arvind,
I agree with you, there is a trade-off: using the approach you think is best for the issue at hand, vs. making sure your code is sufficiently straightforward that others can maintain it, too. I think LINQ did a lot for the popularization of functional concepts in the .Net community, and hopefully F# will push that even more. Still, functional style is not quite mainstream yet, so I tend to follow my gut feeling and go for it when 1) the non-functional solution is really ugly, or 2) the functional solution is understandable, even for someone not too familiar with the domain. Often, I found that functional code emphasizes the intent rather than the mechanics, and as a result, it can be more expressive and understandable.
PS: thanks for taking the time to notify my of the captcha issue, which is now resolved.

Mathias United States | Reply

1/3/2011 9:18:39 AM #

Robert

Personally, I'd go with 'return rules.All(r => r.IsSatisfied);'
unless IsSatisfied wasn't an instance method, in which case I'd go with
'return rules.All(IsSatisfied);'

Smile

Robert Sweden | Reply

1/3/2011 4:54:30 PM #

Mathias

Robert,
I have to agree, your formulation is much more elegant! Thanks for sharing.

Mathias United States | Reply

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



Comments

Comment RSS