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.
Lambda expressions won’t even work until Orcas (VB9) beta 2 comes out in about a month, but I will show you how they work. I will also explain closures; these are pretty important. Before you get started, it is important that you know that these changes in VB are sure to do at least one thing, and that is to give VB programmers the same expressiveness and power that all other language programmers continue to get.
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).
What Are Closures?
A closure is another one of those things that keeps consultants (and authors) in business. A closure is basically a wrapper class that is emitted when you use local variables and return Lambda expressions from functions. Look at Listing 4.
Listing 4: Code that will emit a wrapper class called a “Closure” because you are returning a local variable and Lambda expression from a function.
Public Function ReturnClosure() Dim x As Integer Dim Lambda_Function = Function(y) x + y Return Lambda_Function End Function
In Listing 4, the local variable x is used in the Lambda expression referred to by Lambda_Function. The Lambda Function(y) x + y also exists only in the Function ReturnClosure. Both x and the Lamdba expression exist in local scope and would normally be cleaned up when the function exits and its stack is cleaned up. To support return x and the Lambda expression, the .NET framework emits a wrapper class—the closure—and the wrapper class has a field x and the Lambda expression to extend its life beyond the local function. The compiler handles this for you, but the result is a class that looks something like Listing 5.
Listing 5: The closure, or wrapper, that supports returning the reference to x and the Lambda expression.
Public Class Lambda_Function Public x As Integer Public Function Lambda_Function(y As Integer) As Integer Return y + x End Function End Class
Again, the compiler takes care of all this plumbing, but it’s helpful to know what’s going on.
Lambda expressions are very compressed, shortened-notation functions. They were invented to support more dynamic ways of programming, including putting behaviors in LINQ queries. Lambda expressions are nothing to fear and closures are just wrappers to facilitate passing locally defined expressions around your code.
The .NET framework is still emitting strongly typed code and Orca’s features, such as Lambda expressions, are fully integrated elements of the upcoming version of .NET.
Hope you had fun at TechEd 2007.
About the Author
Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his new book UML DeMystified from McGraw-Hill/Osborne. Paul is a software architect for Tri-State Hospital Supply Corporation. You may contact him for technology questions at [email protected]
If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.
Copyright © 2007 by Paul T. Kimmel. All Rights Reserved.