18

After reading this article, I can't figure out why lambda expressions are ever used. To be fair, I don't think I have a proper understanding of what delegates and expression tree types are, but I don't understand why anyone would use a lambda expression instead of a declared function. Can someone enlighten me?

John Leehey
  • 22,052
  • 8
  • 61
  • 88

7 Answers7

63

First: brevity and locality:

Which would you rather write, read and maintain? This:

    var addresses = customers.Select(customer=>customer.Address);

or:

static private Address GetAddress(Customer customer)
{
     return customer.Address;
}

... a thousand lines later ...

    var addresses = customers.Select(GetAddress);

What's the point of cluttering up your program with hundreds or thousands of four-line functions when you could just put the code you need where you need it as a short expression?

Second: lambdas close over local scopes

Which would you rather read, write and maintain, this:

var currentCity = GetCurrentCity();
var addresses = customers.Where(c=>c.City == currentCity).Select(c=>c.Address);

or:

static private Address GetAddress(Customer customer)
{
     return customer.Address;
}

private class CityGetter
{
    public string currentCity;
    public bool DoesCityMatch(Customer customer)
    {
        return customer.City == this.currentCity;
    }
}

....

var currentCityGetter = new CityGetter();
currentCityGetter.currentCity = GetCurrentCity();
var addresses = customers.Where(currentCityGetter.DoesCityMatch).Select(GetAddress);

All that vexing code is written for you when you use a lambda.

Third: Query comprehensions are rewritten to lambdas for you

When you write:

var addresses = from customer in customers
                where customer.City == currentCity 
                select customer.Address;

it is transformed into the lambda syntax for you. Many people find this syntax pleasant to read, but we need the lambda syntax in order to actually make it work.

Fourth: lambdas are optionally type-inferred

Notice that we don't have to give the type of "customer" in the query comprehension above, or in the lambda versions, but we do have to give the type of the formal parameter when declaring it as a static method. The compiler is smart about inferring the type of a lambda parameter from context. This makes your code less redundant and more clear.

Fifth: Lambdas can become expression trees

Suppose you want to ask a web server "send me the addresses of the customers that live in the current city." Do you want to (1) pull down a million customers from the web site and do the filtering on your client machine, or (2) send the web site an object that tells it "the query contains a filter on the current city and then a selection of the address"? Let the server do the work and send you only the result that match.

Expression trees allow the compiler to turn the lambda into code that can be transformed into another query format at runtime and sent to a server for processing. Little helper methods that run on the client do not.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • +1, I enjoy using ternary operators instead of if statements when I can, so this makes sense to me. – John Leehey May 03 '11 at 18:08
  • I also didn't fully understand expression trees, but your update helps a lot. Thanks! – John Leehey May 03 '11 at 18:14
  • 3
    +1 Lippert for the win. Basically summarized that whole article in 5 mins. Insane. – Ryan Bennett May 03 '11 at 18:17
  • 1
    @Ryan, no kidding. I love the consistent examples too. Great answer. – John Leehey May 03 '11 at 18:23
  • Tip: You can do a little bit better in terms of little helper methods by using TomasP's [solution](http://tomasp.net/blog/linq-expand.aspx), which is also included in [LinqKit](http://www.albahari.com/nutshell/linqkit.aspx) (both free). Something to keep in the back of your mind for when you start getting "method [name] has no supported translation to SQL" errors. This doesn't contradict Eric's point; the library relies on expression trees to perform the translation. – Brian May 04 '11 at 13:24
  • The longer code above is something you can step into with the debugger and see exactly what is happening. Also I don't like the new use of var everywhere. What's the point of having a strongly typed language if you can put var everywhere. I'd prefer if var were auto replaced by the actual type. Guess I'm just old school :-) – Paul McCarthy Nov 09 '17 at 15:40
  • @PaulMcCarthy: You seem to be confusing static typing with manifest typing. C# is still statically typed; "var" does not mean "dynamic". It would be an easy job to use Roslyn to write a rewriter that replaces vars with manifest types; someone has probably already done so. – Eric Lippert Nov 09 '17 at 16:29
  • 1
    @PaulMcCarthy: Also, "you can see exactly what is happening" is certainly not true; what is happening is voltages are changing inside little silicon boxes. What we want is a tool that allows us to understand the action of software *at the level of the business domain*, and not the level of the electrons. Your "what is really happening" is just one of a dozen possible abstraction layers; I want to see what is happening at the business domain level, since my program is *about* the business domain. – Eric Lippert Nov 09 '17 at 16:32
  • @EricLippert My comment implied that I wanted to see exactly what is happening at a debugger level. I'd like to be able to manipulate matter at a quantum mechanical level but it's not going to happen. As projects get larger, having code that is easy to read and follow becomes more important. – Paul McCarthy Nov 10 '17 at 11:06
5

The primary reason you'd use a lambda over a declared function is when you need to use a piece of local information in the delegate expression. For example

void Method(IEnumerable<Student> students, int age) {
  var filtered = students.Where(s => s.Age == age);
  ...
}

Lambdas allow for the easy capture of local state to be used within the delegate expression. To do this manually requires a lot of work because you need to declare both a function and a containing type to hold the state. For example here's the above without a lambda

void Method(IEnumerable<Student> students, int age) {
  var c = new Closure() { Age = age };
  var filtered = students.Where(c.WhereDelegate);
  ...
}

class Closure {
  public int age;
  bool WhereDelegate(Student s) {
    return s.Age == age;
  }
}

Typing this out is tedious and error prone. Lambda expressions automate this process.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • In the line 'var filtered = students.Where(s => s.Age == age);', s is not declared nor does it have a type. s would be of type student? – chribonn Jan 26 '23 at 13:35
4

Let's leave expression trees out of the equation for the moment and pretend that lambdas are just a shorter way to write delegates.

This is still a big win in the realm of statically typed languages like C# because such languages require lots of code to be written in order to achieve relatively simple goals. Do you need to compare sort an array of strings by string length? You need to write a method for that. And you need to write a class to put the method into. And then good practice dictates that this class should be in its own source file. In any but the smallest project, all of this adds up. When we 're talking about small stuff, most people want a less verbose path to the goal and lambdas are about as terse as it can get.

Furthermore, lambdas can easily create closures (capture variables from the current scope and extend their lifetime). This isn't magic (the compiler does it by creating a hidden class and performing some other transformations that you can do yourself), but it's so much more convenient than the manual alternative.

And then there are expression trees: a way for you to write code and have the compiler transform this code into a data structure that can be parsed, modified and even compiled at runtime. This is an extremely powerful feature that opens the door to impressive functionality (which I definitely consider LINQ to be). And you get it "for free".

Jon
  • 428,835
  • 81
  • 738
  • 806
3

http://msdn.microsoft.com/en-us/magazine/cc163362.aspx

Great article on what lambdas are, and why you can/should use them.

Essentially, the lambda expression provides a shorthand for the compiler to emit methods and assign them to delegates; this is all done for you. The benefit you get with a lambda expression that you don't get from a delegate/function combination is that the compiler performs automatic type inference on the lambda arguments

Ryan Bennett
  • 3,404
  • 19
  • 32
1

They are heavily used with LINQ, actually LINQ would be pretty bad without it. You can do stuff like:

Database.Table.Where(t => t.Field == "Hello");

LueTm
  • 2,366
  • 21
  • 31
0

Lambda makes code short and sweet. Consider the following two examples:

public class Student

{


   public string Name { get; set; }
   public float grade { get; set; }
   public static void failed(List<Student> studentList, isFaild fail)
   {
       foreach (Student student in studentList)
       {
          if(fail(student))
          {
    Console.WriteLine("Sorry" + " "+student.Name + " "+  "you faild this exam!");
          }
       }
   }

public delegate bool isFaild(Student myStudent);

class Program
{
    static void Main(string[] args)
    {
        List<Student> studentsList  = new List<Student>();
        studentsList .Add(new Student { ID = 101, Name = "Rita", grade = 99 });
        studentsList .Add(new Student { ID = 102, Name = "Mark", grade = 48 });
        Student.failed(studentsList, std => std.grade < 60);  // with Lamda

          }
       }

private static bool isFaildMethod(Student myStudent) // without Lambda

{

if (myStudent.grade < 60)

        {

            return true;

        }
        else
        {
            return false;
        }
    }
kidistB
  • 26
  • 2
0

They make it easy to pass a simple piece of functionality to another function. For example, I may want to perform an arbitrary, small function on every item in a list (perhaps I want to square it, or take the square root, or so on). Rather than writing a new loop and function for each of these situations, I can write it once, and apply my arbitrary functionality defined later to each item.

afranz409
  • 772
  • 6
  • 11