Lambda Expressions - VB

[Table of Contents] [Next Topic]

In order to learn functional programming and a more declarative style of writing code, we need first to cover some basic material.  One of the first concepts is that of lambda expressions.  Lambda expressions can be summarized in one sentence:

This blog is inactive.
New blog: EricWhite.com/blog

Blog TOCLambda expressions are simply functions/methods.

They have a different syntax, primarily so that they can be written in expression context (more on this shortly) instead of as a member of a class.  However, that is all they are.  For instance, the following lambda expression:

Function (c) c + 1

is a function that takes one argument, c, and returns the value c + 1.

Actually, they are slightly more complicated than this, but not much more.  For the purposes of this tutorial, you only use lambda expressions when calling a method that takes a delegate as a parameter.  Instead of writing a method, creating a delegate from the method, and passing the delegate to the method as a parameter, you can simply write a lambda expression in-line as a parameter to the method.

To show lambda expressions in context, consider the problem where you have an array with 10 digits in it, and you want to filter for all digits greater than 5.  In this case, you can use the Where extension method, passing a lambda expression as an argument to the Where method:

Sub Main()
Dim source() As Integer = _
{3, 8, 4, 6, 1, 7, 9, 2, 4, 8}
For Each i In source.Where(Function(x) x > 5)
Console.WriteLine(i)
Next
End Sub

To understand the semantics of this code, you needn't find some method elsewhere in the source code that does the selection; the code that you need to read is much tighter and smaller.  It reflects your intent in a much cleaner fashion.

Later on in this tutorial, you'll see a number of uses of the standard query operators.  Many of the standard query operators, including Where, take delegates as an argument, so this means that we can call them passing a lambda as an argument.

First, a quick review of delegates:

Defining, Creating, and Using a Delegate

In VB, a delegate is a data structure that refers to either a static method, or an object and an instance method of its class.  When you initialize a delegate, you initialize it with either a static method, or a class instance and an instance method.

The following code shows the definition of a delegate and a method that can be used to initialize the delegate:

' Defines a delegate that takes an int and returns an int
public delegate Function ChangeInt(x As Integer) as Integer

' Define a method to which the delegate can point
Function DoubleIt(x As Integer) As Integer
Return x * 2
End Function

Now, you can create and initialize an instance of the delegate, and then call it:

Dim myDelegate As ChangeInt = New ChangeInt(AddressOf DoubleIt)
Console.WriteLine("{0}", myDelegate(5))

This, as you would expect, writes 10 to the console.

Using a Lambda Expression

With Lambda expressions, the syntax gets even terser:

' Defines a delegate that takes an int and returns an int
public delegate Function ChangeInt(x As Integer) as Integer

Sub Main()
Dim myDelegate As ChangeInt = Function (x) x * 2
Console.WriteLine("{0}", myDelegate(5))
End Sub

This lambda expression is an anonymous method that takes one argument x, and returns x * 2.  In this case, the type of x and the type that the lambda returns are inferred from the type of the delegate to which the lambda is assigned.

If you wanted to, you could have specified the type of the argument, as follows:

' Defines a delegate that takes an int and returns an int
public delegate Function ChangeInt(x As Integer) as Integer

Sub Main()
Dim myDelegate As ChangeInt = Function (x As Integer) x * 2
Console.WriteLine("{0}", myDelegate(5))
End Sub

Using a Lambda with Two Arguments

When using the Standard Query Operators, on occasion, you need to write a lambda expression that takes two arguments.

If you have a delegate that takes two arguments:

' Defines a delegate that takes two Integers and returns an Integer
public delegate Function MultiplyInts(arg1 As Integer, _
arg2 As Integer) as Integer

You can declare and initialize a delegate:

Dim myDelegate As MultiplyInts = Function (x, y) x * y
Console.WriteLine("{0}", myDelegate(5, 2))

There is an overload of the Select extension method where you can specify a Lambda expression that takes two arguments.  The second argument is an index of the item in the sequence.  The following code assembles a string in the lambda expression that contains the item and the index:

Dim i() As String = { "aaa", "bbb", "ccc", "ddd", "eee" }
Dim q = i.Select(Function(x, c) x + " : " + c.ToString)
For Each z In q
Console.WriteLine(z)
Next

When run, it produces:

aaa : 0
bbb : 1
ccc : 2
ddd : 3
eee : 4

You can use this overload to chunk a sequence into groups of arbitrary length.  I’ll show this example later in this tutorial.

Statement Lambda Expressions

C# 3.0 has statement lambda expressions, but Visual Basic does not have this language feature.  However, you can accomplish the same thing by calling another function from the lambda expression.  For example, the following C# snippet shows the use of a statement lambda expression:

int[] source = new[] { 3, 8, 4, 6, 1, 7, 9, 2, 4, 8 };

foreach (int i in source.Where(
x =>
{
if (x <= 3)
return true;
else if (x >= 7)
return true;
return false;
}
))
Console.WriteLine(i);

You can accomplish the same thing in Visual Basic as follows:

Function MyPredicate(x As Integer) As Boolean
If x <= 3 then
Return True
ElseIf x >= 7
Return True
Else
Return False
End If
End Function

Sub Main()
Dim source() As Integer = { 3, 8, 4, 6, 1, 7, 9, 2, 4, 8 }
For Each i In source.Where(Function (x) MyPredicate(x))
Console.WriteLine(i)
Next
End Sub

One difference between C# 3.0 and VB 9.0 is that you can define statement lambda expressions in C# that return void; VB 9.0 has no corresponding feature.

The Func Delegate Types

The framework defines a number of parameterized delegate types:

public delegate Function Func(Of TR) as TR
public delegate Function Func(Of T0, TR)(a0 As T0) As TR
public delegate Function Func(Of T0, T1, TR)(a0 As T0, a1 As T1) As TR
public delegate Function Func(Of T0, T1, T2, TR)(a0 As T0, a1 As T1, a2 As T2) As TR
public delegate Function Func(Of T0, T1, T2, T3, TR)(a0 As T0, a1 As T1, a2 As T2, a3 As T3) As TR

In the above delegate types, notice that if there is only one type parameter, it is the return type of the delegate. If there are two type parameters, the first type parameter is the type of the one and only argument, and the second type is the return type of the delegate, and so on. Many of the standard query operators (which are just methods that you call) take as an argument a delegate of one of these types.

Expression Trees

Lambda expressions can also be used as expression trees.  This is an interesting topic, but is not part of this discussion on writing pure functional transformations.

[Table of Contents] [Next Topic] [Blog Map]