Lambda Expressions and Currying in VB9
I am a linguaphile. That is, I love cool language features such as operator overloading, generics, Lambda Expressions, ternary operators, and anything and almost everything about OOPy languages. For that reason, C++ has probably always been my favorite language, but I seldom write C++ anymore. One reason is that I am seldom asked, but another reason is that many more companies have been using Delphi, VB, Java, and C#. Perhaps this is so due to the unforgiving nature of C++ or the perceived productivity of RAD tools. I'm not sure.
However, as a linguaphile, I am thrilled and concerned (for traditional VB programmers) because esoterrorism has found VB9. Esoterrorism is code that is so esoteric it is almost impossible to comprehend and terrorizes code maintainers. The kind of code referred to is Lambda Expressions that use currying. Lambda Expressions are short, inline substitutions for delegates that exist to support LINQ. Currying is chaining Lambda Expressions together where each expression solves a small part of the problem.
If you think generics are/were hard, wait until you tackle Lambda Expressions (and currying). Because this article is on currying, it is assumed you have some understanding of Lambda Expressions. As a refresher, simply think of Lambda Expressions as short, terse inline functions that use the word Function, followed by parentheses with parameters, and a statement that represents the function body. Prune out everything else about a traditional function and you have a Lambda Expression. Here is an example:
Function(x, y) x + y
The preceding is basically a function that accepts two arguments and returns the sum of the two arguments, x and y. Short and sweet. Lambda Expressions such as the preceding are relatively easy once you get used to the short syntax. However, if you begin adding information—that is, you try to do something useful—these expressions get harder to read.
Currying is where you daisy chain expressions together. Each expression accepts one argument and returns the next expression in the chain until the last expression, which returns the result. Think of currying as a Turing Machine a là Lambda Expressions. (Look up Alan Turing for more on Turing machines; wikipedia has some great information.) By currying the above expression, you get:
Function(x) Function(y) x + y
To assign the curried-lambda to a variable, you can use anonymous types or you can match the type with the generic delegate Func. The following statement assigns the Lambda Expression above to a variable named adder.
Dim adder As Func(Of Integer, Func(Of Integer, Integer)) = _ Function(x) Function(y) x + y
As you can see, the syntax begins to get a bit challenging very quickly. In the example above, you are initializing the generic delegate Func with a generic delegate. To invoke add, you use the function operator that reminds me of Hungarian notation arithmetic:
Possible Scenarios for Currying
I have posed the question to some smart people at Microsoft: "What problem does currying solve?" Thus far no answer, so I have invented a couple of possibilities of my own.
First, Lambda Expressions don't support multi-line statements. If you want to perform more than a single operation, you need a traditional function or currying might work here. With currying, each Lambda Expression performs one step in the operation, and each of the steps except the last one returns the next Lambda Expression in the sequence. (The Lambda Expressions line up like hands across America.) If you alternate which expressions are returned in the chain, you effectively can execute one of multiple alternatives. This can be accomplished with the IIf operator.
IIf is a ternary operator equivalent to the If..Then.Else construct or C#'s ?: operator. IIf works by testing the first parameter and returning the second parameter if the expression is true and the third if the expression is false. If the second and third expressions are Lambda Expressions, you effectively can support conditional behavior with Currying.
Dim curry1 As Func(Of Integer, Func(Of Integer, _ Func(Of Integer, Integer))) = _ Function(x) Function(y) IIf(y > 10, _ CType(Function(z) x + y + z + 5, _ Func(Of Integer, Integer)), _ CType(Function(z) x + y + 10, Func(Of Integer, Integer)))
This code effectively adds x, y, and z to 5 or 10 depending on the value of y. If y is greater than 10, 5 is added; otherwise, 10 is added to x, y, and z. Esoterrorism at its finest, though, this code begs the question is this code serviceable, or worth the effort. While cool, probably not.
A second and more likely scenario is that Currying breaks Lambda Expressions into bit-size pieces that may be suitable for code generators. (The output is of questionable suitability to humans, but your workstation won't care.)
Lambda Expressions and Currying: To Use or Not to Use
Until Lambda Expressions are ubiquitous and everyone is really comfortable with them, be careful where and how you use Lambda Expressions. Their best use is to support LINQ behind the scenes, but you can use Lambda Expressions and currying, if you want to. I reserve the right to use the code I want even if it makes someone else's head hurt.
Mads Torgersen, a Microsoft C# compiler team program manager, characterizes Lambda Expressions as "doing very much with very little, sacrificing only readability." (For an example, see his blog http://blogs.msdn.com/madst/ posting "Recursive lambda expressions".)
Lambda Expressions are shorthand for delegates. They also support LINQ behind the scenes. Currying Lambda Expressions is breaking up expressions into one-variable chunks, chaining the expressions together resulting in the final expression that produces the output. Although curried expressions may be too confusing for general consumption by hominoids, they may prove useful for code generators (and are at least to some extent for LINQ's generators.)
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 upcoming book LINQ Unleashed for C# due in Spring 2008. You may contact him for technology questions at firstname.lastname@example.org.
If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet opened a users group branch in Flint, Michigan in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates.
Copyright © 2007 by Paul T. Kimmel. All Rights Reserved.