Virtual Developer Workshop: Containerized Development with Docker
Lambda expressions are little bits of code that are very powerful. My personal theory on the human mind is that we have to keep putting greater and greater meaning into smaller and smaller packages. For example, Love is a very complex concept captured in four letters. A car is a very useful invention captured in three letters but there are all kinds of physics—combustion, inertia, fluid dynamics—and emotional concepts captured by the word car.
Lambda expressions are like compressed lines of code that mean or do very powerful things. Lambda expressions are based on Lambda calculus—check with Wikipedia, if you are interested—and the concept of closures. These are not as scary as they sound, and I will explain them in a way that will make pretty straightforward sense to you. What is challenging is that Lambda expressions are an evolutionary step in .NET and one of evolutions—anonymous delegates—was skipped for VB programmers.
Why Do You Need Lambda Expressions?
I won't belabor the point: You need Lambda expressions.
Have you noticed how hardware continues to double (approximately) in power but you are still writing code the same old way and still struggling to do in manageable ways? The solution is that you need a more powerful and less labor-intensive way to do powerful things. This is what LINQ is. LINQ, or Language Integrated Query, is like SQL for .NET. Instead of querying just databases like SQL does, LINQ can query anything, including SQL databases, XML, and objects. LINQ is a condensed, fully integrated element of .NET (at least, it will be), and LINQ needs a way to perform powerful operations in a shorthand way. I don't have time to go into LINQ here. You will have to trust me for now.
Traditionally, one way in which you express powerful and reusable elements of code is the method. In .NET 2.0, the anonymous method was introduced (but not for VB. I have my theories on why VB 8.0 didn't get anonymous methods, but that's not important right now). An anonymous method is a method that has no expressed return type or function name. Anonymous methods were designed for simple event handlers. (Refer to my article "What Anonymous Methods Might Look Like in VB.NET" for more information.) Anonymous methods also were condensed regular methods. The problem is that anonymous methods were still pretty big, in fact too big and cumbersome to fit in the middle of an SQL-like statement. The upshot is that some smart, industrious person figured out how to cram more into a tidy, small package, in large part to support LINQ. This person also probably had a class on Lambda calculus in college.
The upshot is:
- You need to do powerful things more simply and Microsoft came up with LINQ.
- A query language needs the power of behaviors—like functions—but it would be weird to inline fully defined functions. The solution is a condensed notation based on a well-known mathematical form, Lambda expressions
How Do I Use Lambda Expressions?
For the most part, think of Lambda expressions as very small methods, even smaller than anonymous methods, that support LINQ. Then, the answer to the question is that you use Lambda expressions just like methods. The other answer is that you will most likely use Lambda expressions in places where traditional methods won't work, such as LINQ queries.
Listing 1 shows you a regular method and Listing 2 shows you the same method as a Lambda expression.
Listing 1: A regular function that checks to see whether the argument string is five characters long.
Function Test(ByVal s As String) As Boolean Return s.Length = 5 End Function
Listing 1 doesn't need any explanation. Now, imagine stripping out all of the non-essentials—like shorthand—and trying to make the equivalent code as small as possible. You might come up with something like the code in Listing 2.
Listing 2: Listing 1 re-written using a lambda expression.
Function(s) s.Length = 5
The VB9 compiler—and the .NET framework 3.5—use the keyword Function, an argument, and a statement. The type of s is inferred from context and use. The Return keyword is implicit and the function body is not necessary. (Keep in mind that even human languages have shorthand: stenography, sign language, light signals, acronyms.)
Note: I asked Tim Ng, the VB9 lead compiler programmer, how the heck you read the Lambda statement. He said they still don't know. I suggested reading (and writing) it like a calculus statement; for instance, the function of s is s.Length = 5. Perhaps they could shorten it even more to f(x) statement.
Essentially the function name, ByVal, argument type, return type, the Return keyword, and the function footer (end statement) were stripped away. (In C#, the same code would be written s=>s.Length=5. The => operator is read goes to, or as I like to call it, the gosinta.)
Note: When you leave the argument's type out of the Lambda expression, it is referred to as implicit typing. Lambda expressions also support explicitly typed arguments.
Both the function Test in Listing 1 and the Lambda expression in Listing 2 work the same way: Pass in a string and the length will be compared to a string length of 5. It's clear how you use Test. It may not be clear how you use the equivalent Lambda expression.
Lambda expressions were designed to support LINQ and also a dynamic kind of programming where behaviors are pluggable. What I mean is that you can pass a Lambda expression around as easily as you pass data around. This means you could, in effect, change the behavior of code at runtime by changing the executable Lambda expression used. To call the Lambda expression in Listing 2, you need to assign it to a variable. You can explicitly use the Generic Func(Of T, U) type or let the VB9 compiler resolve to Generic Func(Of T, U) by using the new anonymous type capability. Listing 3 demonstrates how to assign and use a Lambda expression as an anonymous type.
Listing 3: Assigning a Lambda expression to an anonymous type and invoking the behavior.
Module Module1 Sub Main() Dim Lambda_Function = Function(s) s.Length = 5 Console.WriteLine(Lambda_Function("Hello")) Console.ReadLine() End Sub End Module
In the sample the Lambda expression, Function(s) s.Length = 5 is invoked like a regular function although the instance Lambda_Function. Lambda_Function is emitted as an instance of the generic Func(Of String, Integer).