Monday, October 29, 2018

Lambda Expressions in VB.Net, an Overview


I have to say, lambda expressions are awesome.  

To be explicit a lambda expression is an anonymous method that is used to create delegates or expression tree types. Additionally, they can be passed as arguments or returned as the result of a function  Ideally, they should be short, highly explicit actions.  Additionally, the convenience factor of lambda over traditional delegates is that they can be worked with without giving the method an explicit name and with the code immediately inline where it is to be used.

I first stumbled across lambda expressions in relation to LINQ-to-SQL.  I was wanting to perform some logic on my returned dataset, and all the documentation pointed that lambda expressions were the only way to accomplish what I was wanting.  

The bad part is that at the time, while VB.Net supported lambda expressions there were very little documentation in relation to them. To the point that I just failed to grok them, and ultimately, just used a user function in SQL Server do the work I needed, and went on. What was probably worse, is that Microsoft didn't really show examples of VB.Net lambda expressions for the longest time.  Sure there was the explicit page for VB.Net lambda expressions, but all the sample codes, and the details on how to do the more arcane (and complicated) use cases  were still in C#.  Luckily, that's starting to change, but I still felt the need to build a more basic primer on them and their use in VB.Net.

First is the base declaration syntax.  As in standard methods, lambda expressions which return a result would use the FUNCTION declaration while ones which do not would use the SUB declaration.

Now, in C# you would get a lambda expression built like this:
n=> n.IsReleased

In that, n is the input parameter, and the function returns the IsReleased property of the input. The same logic in VB.Net would be written as:
Function (movie) movie.IsReleased

As is typically the case the VB.Net code is more verbose, but that verbosity also provides a level of clarity which the C# expression lacks. But despite the difference in verbosity, the syntax is strikingly similar. In the C# code, you start with the input parameter which goes into the logic statement.  In the VB.Net code, you start with the function keyword, and its parameter goes into the logic statement.  The difference is in the declaration of the actual method.  In the C# you use the lambda operator, whereas in VB.Net you use the FUNCTION keyword. 

Another thing to consider is that lambda expressions are just a shorthand for anonymous methods.  Anonymous methods are just those methods without a name, rather they're assigned to a variable of delegate type.  In VB.Net an anonymous method would be written like:
dim IsMovieReleased = Function (m as MovieObject) as Boolean 
                                        Return m.IsReleased 
                                End Function 

Now, when the logic extends to more than one line, then we need to use the Function/End Function syntax. For example: 
Function(movie) 
        Console.Log(String.Format("Checked Released State for {0}", movie.Title))
        Return movie.IsReleased 
End Function 

And since the lambda expression is an anonymous function, it can be assigned to a delegate as well.
Dim isMovReleased As Func(of movie, Boolean) = Function(m) m.IsReleased 
Dim movieObject As New Movie With {.IsReleased = True, .Title="Star Trek"}
Dim isReleased As Boolean = isMovReleased(movieObject)

Microsoft has produced a number of predefined anonymous delegates for our use. The FUNC delegate is for methods which return a value, while the ACTION delegate is for methods which don't. Each of them have a number of different possible parameters for a wide range of use cases.

Now, the ACTION delegate is for assigning anonymous methods which doe not return a value. 
Dim logMovie AS Action(of movie) = Sub(m) Console.Log(m.Title) 
Dim movieObject As New Movie With {.IsReleased = True, .Title="Star Trek"}
logMovie(movieObject)

In VB.Net the primary difference in implementation for these two options would be the fact that one uses Sub/End Sub while the other uses Function/End Function. 

Now, as I stated earlier, I first stumbled across lambda expressions in relation to LINQ-to-SQL queries.  What's happen is that the WHERE extension method for IEnumerable(of T) accepts the FUNC(of T, Boolean) delegate.  This means that a method can be applied to the LINQ Queries applied against the datasource.  Using the movie concept, and pulling the released movies, you would generate a LINQ-to-SQL statement like this:
Dim movieList as List(of Movies) = dc.Movies. Where(Function(mv) mv.IsReleased).ToList()

The final use of lambda expressions, and one that I don't typically use, is Expression trees.  Expression trees are representations of code in a tree-like data structure where each of the nodes of the tree is a single atomic expression. Ideally an expression tree would be immutable once defined, and modifying any specific node would mean the creation of a new expression tree which the modifications are applied to, while leaving the existing tree(s) available to the system.

Being able to compile and run the code represented by an expression tree means that the system can enable dynamic modification of executable code, LINQ executions against a database, as well as create dynamic queries.  

Now, imagine this lambda expression:
Dim deleg As Func(of Integer, Boolean)
deleg = Function(f)  0 < f >= 15 

It's purpose is to determine if the passed in parameter  is between 1 and 15.  But it can be built as an Expression Tree in this manner:
Dim expr as System.Linq.Expressions.Expression(of Func)
expr = Function(f) 0 < f >= 15 
Dim deleg As Func(of Integer, Boolean)
deleg = expr.Compile() 

Lambda expressions are one of the better adds to VB.Net over the years, and can lead to a lot of making the code clearer and helping tighten the code significantly.  An aspect that makes code easier to maintain as well as more readable, at least one the initial hurdle of understanding the explicit needs for declaration and use are groked.  And frankly, anything that helps bring clarity, conciseness, maintainability and readability to code is a good thing in my book. 

No comments:

Blog Widget by LinkWithin