Functional Programming in C# – Part 1: Method Chaining

This is the first part in what is currently planned to be a 4-part series on functional programming, though I may end up adding more parts in future as there are other functional topics I could cover.

This first post is greatly influenced by Dave Fancher’s Functional C#: Fluent Interfaces and Functional Method Chaining but with a few additions of my own that I’ve found useful.

I’m not going to go into the history of functional programming or try to extol it’s virtues – there are many sources out there that do that and I assume that if you’re here reading this then you’ve probably already read plenty about that. However, I will say that I’ve been using functional style C# in a commercial setting for 4 years now and have found it to be a powerful tool for writing clean, reliable code.

Map and Tee

Functional languages can chain methods together in such a way that each method acts on the output of the previous method. For example, this F# function would output 13:

10
|> Add 1
|> Add 5
|> Subtract 3

This behaviour is similar to the fluent syntax found in some libraries but can be applied without having to design the class specially for it.

This is achieved using a couple of generic extension methods called Map and Tee.

Map

public static TOut Map<Tin, TOut>(
    this Tin @this, 
    Func<Tin, TOut> func) 
    =>
    func(@this);

Map takes a function and applies it to the value that Map is called on, returning the result of the function call.

This gives us enough to replicate what we saw in the F# example above:

10
.Map(Add(1))
.Map(Add(5))
.Map(Subtract(3));

I’ll deal with exactly how the Add and Subtract methods are defined shortly. But for now we can see how Map has allowed us to replicate the F# example.

However, we run into an issue if we want to output this result to the console as Console.WriteLine has no return value and so cannot be used as an argument to Map. The |> operator in F# however will happily accept Console.WriteLine.

We could get around this by writing a wrapper function for Console.WriteLine which returns the same value passed in. However, this would lead to writing the same thing over and over just to work with Map. To solve this, we have Tee.

Tee

public static TInOut Tee(
    this TInOut @this,
    Action func)
{
    func(@this);
    return @this;
}

Tee accepts an action, calls it with the value that Tee is called on, and then returns the original value. This allows us to chain logging functions like Console.WriteLine into our code.

For example:

10
.Map(Add(1))
.Map(Add(5))
.Map(Subtract(3))
.Tee(Console.WriteLine);

So that’s it right? Well, not quite.

There’s also a use of Tee where you want to call some function with side effects that does return a value but you just want to continue using the current value. This is arguably less pure functionally, but in reality it’s really useful because of the nature of working in a functional style in a traditionally OO language.

So, we add an overload of Tee that looks like this:

public static TIn Tee<TIn, TOut>(
    this TIn @this, 
    Func<TIn, TOut> func)
{
    func(@this);
    return @this;
}

Dealing with IDisposable

So the above is all fine when dealing with normal objects. However, we find a slight issue when dealing with disposable objects. If we were to just chain them in with Map then they would soon get lost and we wouldn’t end up disposing them correctly.

We could just break out the bits of code which use disposable objects and wrap them in using statements, but then we’re just back to standard procedural programming. So, we add a method called Use.

Use

public static TOut Use<TDisposable, TOut>(
    this TDisposable @this,
    Func<TDisposable, TOut> func)
where TDisposable : IDisposable
{
    using (@this)
        return func(@this);
}

Use takes a disposable object and passes it to a function. It then disposes the object and returns the result of the function.

This allows us to chain using statements right into the middle of other functions without risking their Dispose methods not getting called. For example, imagine we wanted a function that read a name from a file and then output a string saying hello to that name:

path
.Map(File.OpenRead)
.Use(stream => new StreamReader(stream)
	.Use(reader => reader.ReadToEnd()))
.Map(x => $"Hello, {x}");

Ideally we wouldn’t have this as a single function as it does too much, but I think it serves as an example of Use.

Aligning to LINQ

Those of you familiar with LINQ should have seen the similarity between the above functions and functions like Select. This isn’t by accident and Map, Tee and Use are designed to work well in conjunction with LINQ.

There are a couple more functions needed to fully round out the functional toolkit and integrate it with LINQ, though. These are ForEach and Evaluate.

ForEach

public static IEnumerable<TInOut> ForEach(
    this IEnumerable<TInOut> @this,
    Action<TInOut> func)
    =>
    @this.Select(x => x.Tee(func));

public static IEnumerable<TInOut, TIgnored> ForEach(
    this IEnumerable<TInOut> @this,
    Func<TInOut, TIgnored> func)
    =>
    @this.Select(x => x.Tee(func));

A ForEach is often a standard extension method in projects. This one is designed to fit in with the LINQ functions and so uses Select to produce an IEnumerable without enumerating the collection.

In my experience, this does mean that this behaviour needs communicating clearly to other people on the project as some people expect it to enumerate immediately.

I find it’s also useful to have another method which performs the enumeration without returning a value, just so the intention is clear. This is the Evaluate method.

Evaluate

public static void Evaluate<T>(
    this IEnumerable<T> @this)
    =>
    @this.ToList();

As you can see, this is just a wrapper for ToList. However, it makes it clearer that the output isn’t required.

Comparison of Functions

Now we have all the functions we need (for now), we can see how they relate to each other and to existing LINQ methods.

Map operates on an element and transforms it into another by executing the given function on it. This is much like Select which applies a function to each element in a similar fashion. In fact, some people like Map to be able to operate on one or more items and basically remove Select. However, I prefer having the ability to Map a collection and operate on the entire collection and retain Select for operating on each item.

Similarly, Tee and ForEach execute a method with the current value as the parameter and then return the value.

Currying

The final topic I’ll discuss in this post is the idea of currying.

Currying is where a function which takes multiple parameters is converted into a sequence of functions which each take a single parameter and build up the result. This allows us to use structures like Map and Tee to call these multi-parameter functions.

Functional languages do this without any special syntax, but we need to add stuff to C# to get the same behaviour.

There are two approaches to this that I’m going to cover. The first one I’ll talk about now, the second one I’ll leave for a later post.

So, we’ll look at how to deal with this when we have control over the functions and so can structure them how we like. This method is OK to use when there are two or three parameters for a function but I wouldn’t recommend it beyond that as it starts to make the code difficult to comprehend.

We’ll take the Add function we used in the earlier examples. Obviously a traditional C# implementation of this would look like:

int Add(int x, int y) => x + y;

However, if we used this function as is with Map we would have to add lambdas like so:

10
.Map(x => Add(x, 1))
.Map(x => Add(x, 5));

This isn’t necessarily bad but isn’t as clean as the examples I showed above.

To get to that cleaner syntax we have to jump further into the functional world of functions being passed around like any other variable. Rather than returning an int, we’re going to return a function that accepts an int and returns an int. This turns the function into:

Func<int, int> Add(int y) => x => x + y;

Now, if you remember back to the declaration of Map, you’ll remember that it takes as an argument a function with a single parameter which is exactly what this function returns! So we can just put the call to the method directly in the Map function as shown in the examples and the second argument will be passed through.

There are also other benefits of writing functions like this. In the following example we provide a function which calculates force given mass and acceleration and then produce other functions from this which have already had acceleration provided and are waiting for mass:

void Main()
{
	var getForceOfGravityOnEarth = GetForce(GravityOnEarth);
	var getForceOfGravityOnMars = GetForce(GravityOnMars);
	var getForceOfGravityOnMoon = GetForce(GravityOnMoon);
	
	12.3
	.Map(getForceOfGravityOnEarth)
	.Tee(Console.WriteLine);
}

Func<double, double> GetForce(double mass) => acceleration =>
	mass * acceleration;

const double GravityOnEarth = 9.8;
const double GravityOnMars = 3.1;
const double GravityOnMoon = 1.6;

As mentioned, there is another was of doing this which means that functions can be written normally, but there’s probably enough involved in this that it deserves its own post.

Wrapping Up

That’s it for this part. You should be able to use the tools described to chain simple methods together to produce functional methods.

There are still things we’re missing, though. We still have no way for handling error conditions; there’s still no good way of handling async methods; and we still have no way of using currying with methods we didn’t write.

These topics will be covered in future posts and I’ll link them here once they’re written.