Introduction
C# was born in an object oriented programming family and started evolving to adopt the characteristics from other programming languages as well the functional programming family. It laid its foundation to adopt functional programming since version 1.0 with delegates and some partial adoptions found in C# 2.0. Finally, it smartly adopts functional programming style with OO.
In this article, I will explain how to write programs in a functional manner using C# 3.0. If you are new to functional programming or do not know the benefits of it, read my two part articles, Introduction to Functional Programming using F# Part 1 and Part 2. These articles are mainly focused on explaining the fundamentals and benefits of functional programming and the famous lambda calculus theory.
Factors for Functional Programming Adoption
As I explained in my article, Introduction to Functional Programming using F#, there are eight factors that need to be supported by a programming language for functional programming. There are:
- Anonymous function name
- Type inference
- Parameterized types
- High order functions
- Immutable data structure
- Recursion
- Currying
- Lazy evaluation
I am going to explain how C# 3.0 adopts or satisfies these factors with necessary details.
Delegates
Delegates are managed callback methods which are introduced in C# 1.0 and much improved in C# 2.0. This enables to wire up a caller to the target method dynamically. There are two aspects in delegates:
- Delegate type. This declares delegate with the signature of the target method.
- Delegate instance. Wiring up a method.
Code 1 shows a sample program which declares a delegate and assigns a caller to invoke a target method.
delegate int MathFunc(int x);
static void Main(string[] args)
{
MathFunc mf = Square;
int result = mf(3);
Console.WriteLine(result);
Console.ReadLine();
}
static int Square(int x) {return x * x;}
Actually, the code MathFunc mf = Square
means MathFunc mf = new MathFunc(Square)
. The later is C# 1.0 syntax. Delegate is fundamental for the concepts in C# 3.0 that satisfy the functional programming factors.
Anonymous Methods
In Code 1, you need to give the method name Square
to the instance mf
. This makes the code a bit clumsy. C# 2.0 introduces anonymous methods in which you can assign a method (actually a method body) without any name using the delegate
keyword. See Code 2.
delegate int MathFunc(int x);
static void Main(string[] args)
{
MathFunc mf = delegate(int x) { return x * x; };
int result = mf(3);
Console.WriteLine(result);
Console.ReadLine();
}
This syntax reduces the coding overhead in instantiating delegates by eliminating the need to create a separate method. This feature satisfies the functional factor #1 anonymous function name.
Generics
Generics is the most notable and powerful feature introduced in C# 2.0. This allows to define type-safe data structure, without specifying the target type for the data structure. This opens another way of reusability using the "template" model. This improves type safety so that there is no need to do sluggish type casting. Code 3 shows a very simple example using generics.
IEnumerable myints =
new List { 1, 1, 2, 3, 5, 8, 11 };
foreach (int i in myints)
Console.Write("{0}\t", i);
Lambda Expression
One of the most notable features introduced in C# 3.0 is lambda expression. This is C# 3.0's version of anonymous methods written in place of a delegate instance. To know more about lambda expression, visit this link. Code 4 demonstrates the usage of lambda expression.
delegate int MathFunc(int x);
static void Main(string[] args)
{
MathFunc mf = x => return x * x;
int result = mf(3);
Console.WriteLine(result);
Console.ReadLine();
}
The lambda expression makes the code much lighter. The interesting point is that you do not need to give type information for the arguments (or bound variables - hope you remember) in Code 4, x
. There is no magic behind lambda expressions and the compiler itself. The compiler knows and infers the type from the delegate
type. This is called type inference which satisfies the functional factor #2.
Generic Lambda Expression
Using lambda expression, Code 4 looks simple. However, whenever a lambda expression is required, we need to define a delegate
type for this. This is very awkward and less productive. .NET 3.5 introduces generic delegate
which simplifies the lambda expression much better and simpler. It provides two pre-defined generic delegate
s Func
and Action
. Using these delegate
s, you do not need to explicitly define a delegate
. The difference between Func
and Action
is the return type. Func
supports return type however Action
supports methods with void
return type. These delegate
s provide a set of pre-defined signatures that are ready to use.
delegate TResult Func<T>();
delegate TResult Func<T1,TResult>(T1 arg1);
delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);
delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);
delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
delegate void Action<T>(T1 arg1);
delegate void Action<T1,T2>(T1 arg1, T2 arg2);
delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3);
delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
Let us see how this and other C# 3.0 features satisfy the remaining functional factors in Part 2 of this article.
References
You can download and see my two part screen cast about Functional Programming using C# 3.0 from my skydrive here.