Functional Programming and its concepts are becoming more important to software industry and data driven applications. But for us to be able to benefit from functional programming we don’t have to use a strictly functional language like Haskell for example. We can follow the principles and patterns of functional programming even in object oriented languages such as C#. The result might not be as convenient and terse as a functional language, but still it beats non-functional style of constructing our code.
C# allows higher order functions through delegation. That means we can accept functions as parameters or return functions. In other words, functions are first class citizen in C#, that means we can structure together a program in a functional manner just like functional languages, but not as terse or convenient I have to admit.
So in this post, first I’m going explain what functional programming is and what benefit we can gain from following this style of programming. Then we’re going to see some of these functional programming paradigms and how it’s written in C#. I also discuss some patterns that enforce functional ideas and how it can be beneficial in C#.
Table of Contents
What Is Functional Programming
Functional programming is actually older than computers. It came basically from mathematical logic (lambda calculus) . In functional programming we program with functions and they don’t change or mutate anything, they just map input to output. Here is a good definition which I’ve found in a quora question.
Functional programming is a paradigm which concentrates on computing results rather than on performing actions. That is, when you call a function, the only significant effect that the function has is usually to compute a value and return it. Of course, behind the scenes the function is using CPU time, allocating and writing memory, but from the programmer’s point of view, the primary effect is the return value.
Most definitions of functional programming concentrates on the functional purity. However there are other aspects of functional programming which is mostly related to its style of writing the program. I’ll introduce some of these styles in subsequent parts of this article.
Benefits of Functional Programming
Here I list the benefits of using functional programming.
1 – Pure functions makes it easier to reason about our code
One of the most important things that help us understand a piece of code better is to know where the source of change is. It is important because if we know for sure what a piece of code does and doesn’t do, then we can change our code with more confidence. Another benefit that this kind of code clarity can bring is when we want to find a problem in a system or debug.
The ratio of time spent reading (code) versus writing is well over 10 to 1 … (therefore) making it easy to read makes it easier to write.
This quote was from the book Clean Code by Robert Martin. This means if I can somehow reduce the amount of time needed to read code, then we have more time to get other things done with less frustration.
2 – Testing pure functions are easier
In pure functions there is no hidden state or any kind of dependency on the outside world. This fact alone is going to make testing much easier because we don’t have to worry about Mocking dependency or other hacks to prepare the functionality for test.
3 – Debugging is easier in programs written in functional style
Writing pure functions which makes a program more clear also can help with debugging. If we know with certainty what a function does, then all we have to do is to see what parameter was passed in and follow that value to see what went wrong.
4 – Declarative approach make it easier to understand the code
In object oriented style of programming, we have statements that directly change and manipulate the values. This style of programming is called imperative programming. But there is another style which is called declarative programming. In this style instead of changing the program directly we write small pieces of functionality that do a specific thing and does it every well. In a way that it can be used with different values and we compose this functions together. As a result we just declare what is needed to be done instead of directly amass all the needed calculation in one place. This leads to a more readable code and can help us to concentrate on solving the actual business problem.
5 – Method signature honesty
This concept mean that method signature of pure functions are meaningful. Often in non functional codes we see methods with no return value or parameter that does something. By looking at the signature, we don’t know what the input and output of this function is and what it does. Also the name of the methods are not a reliable source of information in this regard. So the only choice we have is to read the entire function.
But in pure functions we immediately receive a lot of information about what a function does just by looking at its signature. Sometimes that is all we need to know about a function and that’s far more clear and faster then the non pure functions.
6 – Writing concurrent applications is easier in functional programs
I’ve already talked about this in my asynchronous programming series, you can find this post here. Pure functions are by their definitions thread safe and ready to be used for concurrent programs.
A functional program is ready for concurrency without any further modifications. You never have to worry about deadlocks and race conditions because you don’t need to use locks! No piece of data in a functional program is modified twice by the same thread, let alone by two different threads. That means you can easily add threads without ever giving conventional problems that plague concurrency applications a second thought!
This quote come form the article called FP For The Rest of Us. By writing most of our program in pure functions, we only need to worry about this kind of issues when we have to mutate state.
Downsides of Using Functional Programming
There also some downsides to functional programming. Here I list some problems that we might encounter when using functional programming. Some of these problems is related to how we use the functional style. But most of these problems are related to the current atmosphere of the software industry and not inherent problem of the functional way of writing program.
1 – There is a learning curve for truly understanding and beginning to utilize the functional programing style, much more so then object oriented language. There are not as much learning resources for functional programming when we compare it to its counterpart. But this is becoming less of a problem year by year as functional programming gaining more and more popularity. Not to mention some intimidating mathematical jargon.
2 – Although writing pure functions are easy, composing them to achieve a complete program is difficult. Especially when we first start to write our programs in this style.
3 – Because we are not supposed to mutate state and always copy the value and return a new object/value, we need to write extra code. But following this practice will yield great benefits and save us time in the long run.
4 – Functional programming does not mix very well with any kind of I/O operation. Any function that needs to deal with these type of problems are impure. But we can have a layer in our application that deals with this kind of problems.
5 – Using immutable values and recursion can lead to more memory consumption.
6 – It is not always pragmatic to write the program in functional style or in a functional language. But as I’ve said before, these problems is not inherent to functional style of programming but the available resources etc. that may change over time.
Examples of Using Functional Programming in C#
In this section I’m going to mention couple of functional programming principles that we can use in C#. This list is by no mean comprehensive. I will reference to more comprehensive resources such as books at the end of this article.
Immutable Types
When we create a new type, we should try to make the internal data of our type immutable as much as possible. Also if we want to change the inherent characteristic of our type, it is better to return a new instance instead of mutating the existing type. This result in benefits such as more transparent type which is thread safe out of the box. Also if we can do something which results in correct usage of our type, we better take the responsibility of creating our type in a manner that prevent misuse. Here’s an example of a type that was mutating values at first and refactored to be immutable.
Expression Instead of Statements
This one is much more about the functional style of programming. Consider the below code.
In the Commented out code, we see that first we declare a variable and then mutate the value to produce a result. This code is not only verbose, but also declare and mutate a variable which is unnecessary. We can change this code from an statement into a expression by using the Elvis operator. The refactored version is much shorter and readable.
Using higher order functions to build reusable piece of functionality
In mathematics and computer science, a higher-order function is a function that takes one or more functions as arguments or returns a function as its result. This concept is very powerful for functional composition. But it is also can be used to facilitate common functionality. Consider the below example.
This is the source code of LINQ’s count method. Suppose we wanted to count something in a lot of places based on some criteria. We have two choice, to write a function that count the occurrence for every situation, or write a function that receive the criteria/predicate as a function and returns the number of occurrences.
Functional Composition
In computer science, function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next. So what we get in the end is the result of the most outer call. We can do functional composition in C#, but it will not going to be easy or readable. But there are libraries that help with this process. In below example, I will show how to compose some simple functions together. Then we’ll see the same code written in F#.
In above example, we have three separate functions. We want to combine them to form a unique function which does all the operations. To ease this process we had to use an extension method to do the composition. As you can see, the above code is very verbose and unreadable, now let’s see the same process in F#.
As you can see the composition in F# is very easy, readable and intuitive. There is a feature which help with this process in functional extension for C#, but still, it cannot beat F#.
Method Chaining/Pipe-lining and Extension Methods
One of the important functional programming style is chaining functions and passing the calculated result of one function to the next function. A good example is the string builder in C#. In this instance string builder uses the fluent builder pattern. But we can achieve the same result in other cases with extension methods. The important thing is every time we use the string builder, it does not mutate the string. But return a new string.
In below example we have extended the StringBuilder class using extension methods.
Not creating unnecessary types using Yield keyword
Sometime we need to return a collection of items from a function. The first choice is to create a list type, add the items to the list and return the list.
As you can see in the first example we create a temporary list to hold the items. But this can be avoided if we use the yield keyword. This iterator pattern is also a common pattern that we see a lot in functional languages.
Declarative programming instead of imperative programming using LINQ
One of the most important aspect of functional programming which we already talked about is declarative programming. If you look at the example below, you can see how much shorter and more convenient the declarative version really is.
The important thing is, I don’t really care about how we find the even elements. That is I don’t care about the process of iteration, that’s resemble a noise in my program. All I care about is finding the even elements. In the LINQ version of the program you can see that we only state what we want and not how we want it to be done.
Immutable Collections
Immutable collection are collections that once they are created, their value cannot change. Instead if we want to add or remove something from these collections, a new collection is returned. You can find a list of these collections in this link.
Creating new a collection every time seems incredibly wasteful for complex types in terms of memory. But it doesn’t have to be, because, if you have f(x) = y, and values x and y are “mostly the same” (e.g. trees which differ only in a few leafs) then x and y can share parts of memory – because neither of them will mutate. This article goes into more detail about the inner workings of these collections. Eric Lippert also has a series on immutability in C#.
Thread-Safe Collections
If we write any kind of concurrent/asynchronous program, it is important to making decisions that makes it easier to write a correct program. One of problems that can occur when we write this kind of programs are race condition. That is when two thread competing with each other to access a resource. In these circumstances we can make our resource immutable. The .NET Framework 4 introduced the concurrent collections for this purpose. Some of these types are as follows.
BlockingCollection<T> | Provides bounding and blocking functionality for any type that implements IProducerConsumerCollection<T>. For more information, see BlockingCollection Overview. |
ConcurrentDictionary<TKey,TValue> | Thread-safe implementation of a dictionary of key-value pairs. |
ConcurrentQueue<T> | Thread-safe implementation of a FIFO (first-in, first-out) queue. |
ConcurrentStack<T> | Thread-safe implementation of a LIFO (last-in, first-out) stack. |
ConcurrentBag<T> | Thread-safe implementation of an unordered collection of elements. |
IProducerConsumerCollection<T> | The interface that a type must implement to be used in a BlockingCollection . |
These thread-safe collections are not a silver bullet. We should be mindful of how and when to correctly use them, especially when it comes to memory consumption.
Design Patterns to Increase Clarity Even if We Don’t Use Functional Programming Style
We now understand more than ever that side effects are something that can be problematic especially when it comes to reading and understanding the code. It’s very important to understand where is the source of change. That’s why in object oriented languages we have the idea of command query separation which most importantly states.
The fundamental idea is that we should divide an object’s methods into two sharply separated categories:
Queries: Return a result and do not change the observable state of the system (are free of side effects).
Commands: Change the state of a system but do not return a value.
This pattern is closely related to principles that we know exists in functional programming. For example in some functional languages there are some restrictions on mutating certain programming constructs. The reason being we know for sure that a method does not change a state in our system, then it becomes easier reason about our code. That is we can simply understand where is the source of change in our system and that is very valuable for debugging for example. Another example is testing, because pure functions without any outside dependency is very easy to test.
Important Functional Programming Jargons
Here I list some functional programming jargons which I think are the most important to understand. This is not a comprehensive list, for a more complete list of jargons, please refer to the further reading section.
Higher-Order Functions (HOF)
Higher order functions are functions that take another function as an argument or return a function.
const filter = (predicate, xs) => xs.filter(predicate)
The opposite of higher order function are first order functions which don’t take a function as an argument or return a function.
Closure
A closure is a local scope that keep the local variables even after the code execution is moved out of that block. The scope and all of its local variables are tied to the enclosing scope and are available as long as the enclosing function is available.
const addTo = (x) => {
return (y) => {
return x + y
}
}
Arity
The number of arguments a function takes. From words like unary, binary, ternary, etc. This word has the distinction of being composed of two suffixes, “-ary” and “-ity.” Addition, for example, takes two arguments, and so it is defined as a binary function or a function with an arity of two. Source
Partial Application
Wikipedia define it as follows.
partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
Here’s a simple example.
function addition(x, y) {
return x + y;
}
const plus5 = addition.bind(null, 5)
plus5(10) // output -> 15
Currying
Is a technique for converting function calls with N arguments into chains of N function calls with a single argument for each function call. Currying always returns another function with only one argument until all of the arguments have been applied. So, we just keep calling the returned function until we’ve exhausted all the arguments and the final value gets returned. Source
Here’s a simple example of a curried function.
// Normal function
function addition(x, y) {
return x + y;
}
// Curried function
function addition(x) {
return function(y) {
return x + y;
}
}
Function Composition
In functional programming, function composition is the act of creating a new function by combining two functions together. In function composition the output of one function is going to be used as an input of another function. We already saw how to compose function in C# so I’m going to reuse the same example.
Purity/Pure Functions
In functional programming a function is said to be pure if its result is entirely dependent on its argument and not anything from outside the function.
Side effects
A function is said to have side effect if as part of its internal implementation it also changes external state. In this context external state is any kind of change or effect which is not dependent on its arguments. Pure functions do not have any side effects.
Referential Transparency
In functional programming, referential transparency means that a function can be replaced by its literal calculated result without changing the behavior of the system. If we think about why that we reach to the conclusion, we realize it’s because this function doesn’t depend of any external state and because of that, it is predictable/deterministic and pure as a result.
Lambda
Lambda comes from the Lambda Calculus and refers to anonymous functions in functional programming. It allows us to write quick throw away functions without naming them. We use lambdas all the time in C# when we use LINQ.
Function
A function f :: A => B
is an expression – often called arrow or lambda expression – with exactly one (immutable) parameter of type A
and exactly one return value of type B
. That value depends entirely on the argument, making functions context-independent, or referentially transparent. What is implied here is that a function must not produce any hidden side effects – a function is always pure, by definition. These properties make functions pleasant to work with: they are entirely deterministic and therefore predictable. Functions enable working with code as data, abstracting over behavior:
// times2 :: Number -> Number
const times2 = n => n * 2
Functor
In functional programming, a functor is an object that has the capability to be mapped to something else. Consider the below example.
Console.WriteLine(new[]{2,4,6}.Select(n=> n + 3)
Here we have an array and we map over it and pass in a lambda and transform it by adding 3 to each element. Here the array is a functor.
Monad
Explaining “what is a monad” is a bit like saying “what is a number?” We use numbers all the time. But imagine you met someone who didn’t know anything about numbers. How the heck would you explain what numbers are? And how would you even begin to describe why that might be useful? What is a monad? The short answer: It’s a specific way of chaining operations together. In essence, you’re writing execution steps and linking them together with the “bind function”. (In Haskell, it’s named >>=.) You can write the calls to the bind operator yourself, or you can use syntax sugar which makes the compiler insert those function calls for you. But either way, each step is separated by a call to this bind function. Source
I suggest reading this and this posts on Stackoverflow.
If We Write Mostly Pure Functions Where Do We Mutate/Change The State Then?
At some point in our application, we need to mutate some state. Because nothing will happen in our application if we do not mutate state. Basically these mutation is the pragmatic end goal of the application. What we need to do in these situation is to have a immutable core for our application that doesn’t mutate state, and another outer layer that does.
The important idea is to separate the code that is pure, and the code that mutate state. In this model if we want to mutate something, we pass our data through a pure function and receive the result. Then we can do whatever we want with that result at the outer layer, maybe update the user interface with the received value. What we gain by doing that is that we have a solution that is more readable, easier to reason about and as a result to change and debug.
We can see this basic principle in a lot of places. For example clojure language has the Atoms for this purpose. In JavaScript world we have libraries like Redux, when we have a central store that holds the data. If we want to change the state we have actions that describe the change (we can think of them as pure functions) and the reducers which apply that change to the state.
Other Functional Programming Languages
I think it is beneficial to also have a look at other functional programming languages. Because a lot of people think following functional programming paradigm in object oriented languages is not as convenient as functional languages and they get in our way. Languages such as Lisp, Clojure, Erlang, OCaml and Haskell are used for variety of purposes across different industries. Each of these languages have different flavor of the functional programming style. For example ML is a general purpose functional programming language which F# is a member of. F# is particularly of interest to us because it mixes the terse, expressive and composable style of functional programming with all the feature which is available in .NET.
Functional Programming Libraries for C#
There are some libraries that built to make it even easier to use the functional style in C#. Here I list some libraries that I think might be beneficial.
Further Reading
SOLID: the next step is Functional
Visual Studio Toolbox – FP in C#
GOTO 2018 • FP in 40 Minutes • Russ Olsen
EastBanc Technologies – FP With C# | Workshop
React Next 2018 – FP Fundamentals
Going all in with Functional C#
Real-World FP With examples in F# and C# (Book)
Summary
In this post, I described what functional programming is and what its benefits and downsides are. I also bring up instances where we can use functional programming style in our C# code to improve it, even though it is not specifically a functional language. We saw some patterns that can give us the benefits of functional programming and make it easier to read, understand and reason about our code. We also took a brief look at some functional programming jargons.
One thought on “Functional Programming in C#: A Brief Guide”