To do functional programming in C#, it is important that we have a base of knowledge on which to build. We have to cover the prerequisites. Then, what we have to learn is how to write code and compose queries in such a way that you get the results you want without introducing side-effects, which leads to composability. Composability of code is a core concept that will guide your use of these new language features in the most effective manner.
When you write object-oriented code, you focus on such things as:
· Proper class and interface design, including class hierarchies, encapsulation, defining class invariants, patterns, etc.
· Type members (methods, properties, fields, and events), including method pre- and post-condition
· Procedural code
· Collections, including generic Collections
One of the issues regarding FP is that you have to learn a number of new programming syntaxes and concepts. Not all of these topics are strictly FP concepts; however, doing FP in C# is made significantly easier if you understand these:
· Lambda expressions and anonymous methods
· Extension methods
· Lazy evaluation (using yield return), and in contrast, eager evaluation
· Type Inference and anonymous types
· Object and Collection Initializers
· Tuples (using either anonymous types, or more traditional named types)
Once you have all of these tools in your toolbox, you can use them in concert. The end result is more than the sum of the parts. You can compose queries and transformation using those tools in very cool ways.
But there is much in common between FP and traditional OO programming:
· You can still write classes. Classes can derive from other classes. You have methods, properties and fields in the classes. However, the general rule is that once you have instantiated an object, you never change fields or set properties in the object. You treat all classes (wherever possible) as immutable. Some classes in the .NET framework are inherently mutable by nature, such as classes that implement IDisposable. However, there are approaches that you can take to minimize the impact of these classes on code written in the functional style.
· You still write methods in your classes. They still take any number of parameters, and have a return type, same as always. There are two principle differences: you write in a pure approach that doesn't modify any data outside of the method, and you write extension methods, which is just a fancy way of writing static methods in static classes. Extension methods should also be written in a pure fashion.
· You still write procedural code inside of methods. You can use if statements, switch statements, etc.
· When writing code in methods, you still set variables. However, once you set a variable, you never modify it. Note that in some more pure functional programming languages, variables are really not variables; they are always immutable. Sometimes they are called symbols, which conveys the semantic differences between them and variables. When using C# 3.0, we use variables, and the compiler will not enforce that the variable is immutable. We have to rely on good coding practice. I would love a feature in the compiler where I could indicate that a variable is immutable after initially being set, or that an object is immutable after construction object initialization.
· In many cases, events are associated with change in state in objects. Given that the functional approach requires treatment of all objects as immutable, I haven't had any occasion to use events in a class that I'm using in a functional transform.
What You are Not Going to Learn
This tutorial is not going to attempt to present the theoretical underpinnings of functional programming. Instead, I am going to focus on pragmatic concepts that you can apply with C# 3.0.
This tutorial starts with a few assumptions:
You are a competent C# programmer.
You understand delegates.
You understand generics and generic collections. This is an important one, as the LINQ technology is deeply rooted in generic types. Generics were introduced in C# 2.0. However, generics (sometimes called parameterized types) have been around for years in the form of templates in C++, and in other languages. In addition to generic interfaces and classes, C# has generic methods, also important in functional programming.
When I first learned about object-oriented programming many years ago, I quickly realized that I could write object-oriented code in plain old C. The advantage of C++, Java, C#, and other languages is that they provide syntactic support so that you can naturally express OOP concepts in your code. The same is true with functional programming. Once you have a grasp of the concepts, there is no reason that you can't do 'functional programming' in just about any language that has delegates or function pointers, however, it is extremely convenient to be able to express these concepts directly in the language. Certainly, to do type-safe FP requires a modern language. And there is a lot to like with the C# 3.0 language features that enable functional programming. Further, you have to put up with a LOT of syntactic noise when doing FP using a language that doesn't support FP.
I've also written a blog post that compares and contrasts the two styles of programming. You can see that post here.