Don’t Misuse Lambdas

Avoid Duplicating Code

It’s great that so many C# and VB.NET developers are taking advantage of LINQ. Unfortunately, using LINQ can encourage you to misuse lambdas. Consider the following simple example:

var results = from x in 0.Through(10)
              select x * x;

Square is a useful utility function. It shouldn’t be defined as a lambda because that ensures code duplication instead of reuse. It’s more obvious using extension method-syntax:

var results =
    0.Through(10)
    .Select(x => x * x);

The problem is that in versions of C# prior to 4.0, it is painful to define Square as a reusable method because Select is a function with multiple generic parameters. You are forced to painfully specify the type parameters or use an unnecessary lambda:

//specifying the generic types
var results =
    0.Through(10)
    .Select<int, int>(Math.Square);
 
//using an unnecessary lambda
var results =
    0.Through(10)
    .Select(x => Math.Square(x));

Even in C# 4.0, the query syntax retains the unnecessary lambda problem:

var results = from x in 0.Through(10)
              select Math.Square(x);

Fortunately, the extension method syntax is fixed in 4.0:

var results =
    0.Through(10)
    .Select(Math.Square);

Avoid Multi-Line Lambdas

It’s easy to recognize that Square should not be a lambda, but some functions are less obvious:

IEnumerable<Ninja> MakeFearsomeFightingTeam(SecretOoze ooze,
                                           IEnumerable<Turtle> turtles,
                                           Pizza pizza)
{
    return 
        turtles
        .Select(turtle =>
        {
            var transformed = ooze.Transorm(turtle);
            transformed.Say("Cowabunga!");
            transformed.Eat(pizza);
            return transformed;
        });
}

You should avoid writing multi-line lambdas like this where possible. In this case, it makes the code harder to read, especially if you add another operation after the Select. It’s difficult to understand what the lambda is doing upon first glance. Also, there’s a good chance that you will want to reuse the behavior of the lambda elsewhere in your program.

IEnumerable<Ninja> MakeFearsomeFightingTeam2(SecretOoze ooze,
                                            IEnumerable<Turtle> turtles,
                                            Pizza pizza)
{
    return
        turtles
        .Select(turtle => Ninjaify(ooze, pizza, turtle));
}
 
Ninja Ninjaify(SecretOoze ooze, Pizza pizza, Turtle turtle)
{
    var transformed = ooze.Transorm(turtle);
    transformed.Say("Cowabunga!");
    transformed.Eat(pizza);
    return transformed;
}

The named instance method gives a description to the behavior that was in the multi-line lambda, and the Select statement is more readable. The sacrifice is that Ninjaify has to take extra arguments because it cannot rely on closure. There is also no guarantee that the definition of Ninjaify will remain close to where it is used as you add more code. Additionally, C#’s syntax requires that you use an unnecessary lambda to pass the turtle argument to Ninjaify. The answer is to use a locally defined method:

IEnumerable<Ninja> MakeFearsomeFightingTeam3(SecretOoze ooze, 
                                            IEnumerable<Turtle> turtles,
                                            Pizza pizza)
{
    Func<Turtle, Ninja> Ninjaify = 
        turtle =>        
        {
            var transformed = ooze.Transorm(turtle);
            transformed.Say("Cowabunga!");
            transformed.Eat(pizza);
            return transformed;
        };
 
    return
        turtles
        .Select(Ninjaify);
}

Like a lambda, the locally defined method uses closure to avoid redefining variables, and it has the readability benefit of being defined near to where it is used. Since it is a named method, it provides a useful description of it’s behavior, and it doesn’t disrupt the flow of your Select method. It’s the best of both worlds. The only downside is that it can’t be called outside the scope of the MakeFearsomeFightingTeam3 method.

Unfortunately, C# requires you to write the full type signature for the function. The compiler will fail with a “cannot assign lambda expression to implicitly-typed local variable” if you try to use the var keyword. This is a real pain for more complicated signatures, and it limits you from using locally defined methods to their full potential.

Going Further With F#

In F#, the type signature problem goes away:

let MakeFearsomeFightingTeam4 ooze turtles pizza = 
    let ninjaify turtle =
        let transformed = ooze.Transform(turtle)
        transformed.Say("Cowabunga!")
        transformed.Eat(pizza)
        transformed

    turtles 
    |> Seq.map ninjaify

Here, you can see the benefit of F#’s type inference system and functional programming roots. The behavior of the code is retained, but the extraneous type signature is not required.

F# also fixes the problem of requiring an unnecessary lambda for named methods outside the scope of the MakeFearsomeFightingTeam4 method. Automatic currying makes it easy to pass the arguments that would be captured by closure:

let ninjaify ooze pizza turtle =  
    let transformed = ooze.Transform(turtle)  
    transformed.Say("Cowabunga!")  
    transformed.Eat(pizza)  
    transformed

let MakeFearsomeFightingTeam5 ooze turtles pizza =  
    turtles
    |> Seq.map (Ninjaify ooze pizza)

In Summary

  • Use lambdas for one line, single use functions
  • Prefer locally defined functions to multi-line lambdas, but be wary of complicated type signatures in C#
  • If a function is reusable, move it to the appropriate class or module, and use it instead of a lambda or locally defined function
  • Use F# to avoid messy type signatures and unnecessary lambdas
This entry was posted in C#, F#, Functional. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

14 Comments

  1. Posted September 28, 2010 at 3:13 am | Permalink

    Interesting post. Love the use of TMNT in the sample code. Totally tubular, dude!

  2. Allon Guralnek
    Posted September 28, 2010 at 9:43 am | Permalink

    I would never have thought of simply doing .Select(Math.Square). It just shows you that even though I love LINQ and use it a lot (and lambda expression in general), I am still not thinking functionally. Thanks for the wakeup call!

    Also, it looks to me like your “locally defined method” is just a multi-line lambda expression put into a locally defined delegate, is it not? I really don’t see how foregoing the local delegate makes the code harder to read. In any case, in my opinion it’s rather confusing to call it a “locally defined method”.

  3. Chris Marinos
    Posted September 28, 2010 at 3:50 pm | Permalink

    Allon-

    There are a couple of reasons that I think that locally defined methods are easier to read than multi-line lambdas.

    When I read a LINQ expression that performs a few operations, I want to understand the high level goals of the writer. LINQ and functional programming are declarative by nature, so I like to think of a LINQ expression like a recipe. When I see a multi-line lambda, my attention is immediately drawn to the details of one step of the recipe instead of the overall process. It feels a lot more imperative to me. To continue the recipe metaphor, it’s like having a recipe that tells you to “put some green beans into boiling water for a minute or two, then drop them into ice water” instead of saying “blanch the green beans”.

    Locally defined methods also give you the ability to describe what your lambda is supposed to do by giving them a name. With single line lambdas, intent is usually clear, but I find that multi-line lambdas often don’t communicate their intent well. Again, it’s like using the word “blanch”- it communicates intent much better.

    That being said, readability is always a bit subjective :-)

    -Chris

  4. Bigsby
    Posted September 29, 2010 at 12:00 pm | Permalink

    Nice post, Chris,

    I’m very fond of Lambda Expressions and I use them in any non re-usable code: LINQ, EventHandlers, anonymous methods, etc..

    I got a little puzzled when you state, after the first code snipet: “It shouldn’t be defined as a lambda because that ensures code duplication instead of reuse.”

    What do you mean by duplication here? Should not the compiler take care of all these and make them all the same in the end?

    PS1 – int.Through(int) extension is pretty cute but not implemented.
    PS2 – Math.Square does not exist and existing Math.Sqrt is not valid here for it receives a double as parameter.

  5. Posted September 29, 2010 at 1:21 pm | Permalink

    For directness and simplicity of reading, IMHO the “x * x” is actually better than the Math.Square. It shows exactly what will be done and it is a simple operation. I don’t care that it is duplicated because Math.Square is not going to be changed to x * x * x any time soon, and so if my requirements change I will need to change every instance of Math.Square anyway.

  6. Chris Marinos
    Posted September 29, 2010 at 2:44 pm | Permalink

    Bigsby-

    RE: duplication vs reuse –

    You are correct that the compiler won’t generate the same lambda multiple times. If you use the same code in different lambdas, then the compiler will emit two methods. The reuse that I’m referring to here is pre-compile. If you have a lambda that performs an action, and you want to perform that action elsewhere in your code, you either need to duplicate the code to perform the action, or pull the code into a reusable helper method as I suggest.

    RE: int.Through(int) extension – I implement this code in a library called Elevate (elevate.codeplex.com)

    RE: Math.Square – Correct, it doesn’t exist. I created my own helper for illustrative purposes. I almost always have a test project for all of my sample code, and in the future, I will try to link to the source.

    Thanks for the feedback!

    -Chris

  7. Chris Marinos
    Posted September 29, 2010 at 2:57 pm | Permalink

    Jonathan-

    The imperative programmer in me agrees with you and likes to see x => x * x. Of course, the functional/declarative side of me disagrees.

    Ultimately, for something as basic as squaring two numbers, it’s hard to argue that one approach is head and shoulders above the other. Once you go beyond sample code and into application logic, change is far more likely to occur, and that’s where I think that refactoring into helper methods wins out.

    -Chris

  8. Posted September 29, 2010 at 10:05 pm | Permalink

    Hey Chris, thanks for writing this. I agree with the concept, but I disagree on specifics. E.g. ‘x * x’ is declarative and should really only be refactored if it represents something more than a square expression. I’m also more in favor of using extension methods and making it feel more object-oriented.

    I wrote a full response on my blog: http://www.kodefuguru.com/post/2010/09/29/Response-to-Done28099t-Misuse-Lambdas.aspx

  9. Chris Marinos
    Posted September 30, 2010 at 4:49 am | Permalink

    Chris-

    I think our opinions differ largely due to different levels of exposure to Functional Programming, but I do have a few comments that I left over on your blog.

    -Chris

  10. Posted September 30, 2010 at 2:42 pm | Permalink

    That could be. I’m very interested in the declarative/functional aspects of the C# language and how it affects the way we program things in C#. That’s why your article provoked a response =). I wouldn’t have bothered responding if I didn’t find the information valuable.

  11. Richard
    Posted October 6, 2010 at 7:57 pm | Permalink

    “The compiler will fail with a “cannot assign lambda expression to implicitly-typed local variable” if you try to use the var keyword”

    Unless you use a simple helper method, that is:
    http://tomasp.net/blog/dynamic-linq-queries.aspx


    public static class Linq
    {
    public static Func<T, R> Func<T, R>(Func<T, R> f)
    {
    return f;
    }
    }

    ...

    var Ninjaify = Linq.Func((Turtle turtle) =>
    {
    var transformed = ooze.Transorm(turtle);
    transformed.Say("Cowabunga!");
    transformed.Eat(pizza);
    return transformed;
    });

    You get the compiler error because the expression could be a Func{T,R} or an Expression{Func{T,R}}; the helper method eliminates the ambiguity. Unfortunately, you still have to specify the argument type.

    Another alternative for your example would be:


    public static IEnumerable<T> Pipe<T>(this IEnumerable<T> source, Action<T> action)
    {
    foreach (T item in source)
    {
    action(item);
    yield return item;
    }
    }

    return turtles.Select(ooze.Transform)
    .Pipe(transformed => transformed.Say("Cowabunga!"))
    .Pipe(transformed => transformed.Eat(pizza));

  12. Chris Marinos
    Posted October 7, 2010 at 5:23 am | Permalink

    Richard-

    Great points. I didn’t want to go into that much depth in the post, but the helper methods do get around the problem.

    I’ve tried using a method similar to Linq.Func before, but I didn’t feel that it added enough value to offset the added confusion that it might cause someone reading the code later.

    I do like the Pipe method, though. The only trouble I have is naming on that one. Pipe isn’t bad, but it may cause confusion due to F#’s |> operator.

    -Chris

  13. A commentator
    Posted October 8, 2010 at 1:27 pm | Permalink

    Truly awful post

  14. Chris Marinos
    Posted October 8, 2010 at 2:39 pm | Permalink

    Aww, I’m sorry that you feel that way :-(

3 Trackbacks

Post a Comment

Your email is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*
*