If you're not firmly embedded in the Microsoft world, it would be easy to miss some important developments in the past few years. In particular, many of the best minds in programming language design now work for Microsoft Research, and it's starting to show. Let's take a quick look at C# 3.0 (included in the .Net 3.5 SDK and Visual Studio 2008). This is a language that has rapidly evolved from being near-equivalent to Java, to being near-equivalent to an object-functional language I used to work with called Nice. It's got lexical scoping, a convenient syntax for anonymous functions, type inference, and so on. Read on for examples!
Here's what type inference looks like. Instead of
StringBuilder builder = new StringBuilder();
you can omit the type declaration:
var builder = new StringBuilder();
and the compiler is smart enough to figure out the type of builder. This is still static typing, it's just static typing with less keypressing, as fans of languages like Haskell and the ML family have enjoyed for decades now. But it's a first for mainstream languages as far as I know.
C# has always had anonymous functions in the form of "delegates", a confusing overloading of the term at best. These are well accepted in the C# world, but they're tediously verbose for a functional programmer. Now C# has lambda expressions, too. These are first class objects and can be passed to things that expect delegates as well. Here's an example:
Func<int,int> square = x => x * x;
Note the type signature. This is one thing they missed in this version: functional types have to be explicitly specified, you can't use var with them. However, the important part is that you don't have to specify types for the arguments, so the function definition can be pleasingly succint. Multiple arguments and zero arguments are supported as well, you just use parenthesis.
Since functions are first class objects, functional programmers would expect their old friends to be available, things like map, foldl, filter, and so on. These are bundled under the name "Linq" in the MS vernacular. Linq actually includes some other very interesting bits that I'll cover in another posting. Linq is a good example of technology transfer from the functional programming community - it's largely based on the work of Erik Meijer, a big name in the Haskell world.
So on to the introductions. Meet the map function:
var nums = new int[] {1, 2, 3};
var incremented = nums.Select(x => x + 1);
//another way
var incremented2 = (from x in nums select x + 1);
The second example touches on another important part of Linq - there's a whole alternate syntax for functional expressions that looks similar to SQL. Food for later posts.
Here's filter:
var evensUpTo100 = Enumerable.Range(1,100).Where(x => x % 2 == 0);
And foldl:
Func<int, int, int> product = (x, y) => x * y;
Enumerable.Range(1, 5).Aggregate(1, product);
How about currying? Not built in, but easy enough to implement:
//Convert a 1-arg function to a 0-arg thunk by binding an argument
public static Func<Z> Curry<T, Z>(this Func<T, Z> func, T t)
{
return () => func(t);
}
//Convert a 2-arg function into a 1-arg function by binding the first argument
public static Func<U, Z> Curry<T, U, Z>(this Func<T, U, Z> func, T t)
{
return (u) => func(t, u);
}
The keyword "this" above makes it possible to make it look like Curry is a method defined on all functions. Let's consider a typical "adder" example:
Func<int,int,int> add = (x, y) => x + y;
Func<int, Func<int, int>> makeAdder = x => (y => add(x, y));
Func<int, int> addFive = makeAdder(5);
//Another way, using Curry:
Func<int, int> addFive_ = add.Curry(5);
As a final example for this installment, I'll present ZipWith and Zip, two more staples of functional programming.
public static IEnumerable<V> ZipWith<T, U, V>(Func<T, U, V> f,
IEnumerable<T> en1, IEnumerable<U> en2)
{
//We're working with sets that happen to be implicitly indexed. We need to
//make that index explicit in order to connect the two enumerables.
var e1i = en1.Select((x, i) => new { Index = i, Value = x });
var e2i = en2.Select((x, i) => new { Index = i, Value = x });
//Sql-like syntax and method-call syntax just two ways to say the same thing
var zipped = from e1 in e1i
join e2 in e2i on e1.Index equals e2.Index
select f(e1.Value, e2.Value);
//Another way to say the exact same thing:
//var zipped2 = e1i.Join(e2i,
// lhs => lhs.Index,
// rhs => rhs.Index,
// (e1, e2) => f(e1.Value, e2.Value));
return zipped;
}
public static IEnumerable<Pair<T, U>> Zip<T, U>(IEnumerable<T> ts, IEnumerable<U> us)
{
return ZipWith((x, y) => new Pair<T, U> { First = x, Second = y }, ts, us);
}It's an exciting time to be working with C#. Let me know if you'd like to see other topics relating to C# and functional programming.