C# 9 Creeps Closer to Functional Programming
What happens when a successful OO language cross-pollinates with FP ideas?

There are programming languages, and there are the ways we use them. Sometimes the two line up, and sometimes our expectations and conventions obscure the full range of possibilities.
For example, many programmers who’ve never touched a line of C++ think of it as an object-oriented version of the ancient C language. And they’re not entirely wrong (after all, C++ is a direct evolution of an experiment called C with classes). And yet, any modern-day developer who actually uses C++ knows that it’s a wide-open multiparadigm language that can go in plenty of different directions.
C# is a different story — or is it? It started out as a streamlined, Java-influenced, thoroughly object-oriented language. (For a brief time, it was even codenamed Cool, for “C-like Object Oriented Language.”) But recently, the ground has been shifting. Among the many improvements in C# 9 — the version we’ll see released this year with .NET 5 — are some features that have the distinct flavor of functional programming.
Mads Torgersen, the lead C# language designer has been clear about his interest in functional programming. When asked what he would do if he had the chance to redesign C# from scratch, he said:
“C# started out very object-oriented, and has been adopting functional features over time. Starting over, I would probably try to strike a balance between the two from the get-go.”
(Side note: if you were expecting us to talk about Anders Hejlsberg, the creator of C#, let us update you. Hejlsberg is still active at Microsoft, but now spends most of his days heading up development on the TypeScript language.)
Clearly, the ideas of functional programming are exerting an influence at Microsoft. But is functional programming in C# a promising direction or a mere flash in the pan? Let’s take a look at the two most notable examples in C# 9.
Records: All the data, none of the mutability
One of the key ideas in functional programming is that data structures should be immutable. You create packages of information, confident that they can’t be changed as they travel around your code.
If you’re used to object-oriented programming, where every object is a bucket of hidden variables, this mindset takes some getting used to. But the advantages are significant. Imagine a world where inconsistent state is impossible, and unexpected side effects never happen. (If you’re wondering what I mean by side effects, here’s an example: you call a method on an object that, unknown to you, changes that object’s state. This change then slips through to somewhere else in your program, where it does who knows what.)
C# 9 takes a big step forward on immutability with a new record feature that lets you build objects that can’t change. The word record hints at one common use case — representing records from a database.
Here’s an example, with the new bits in bold:
public record Employee
{
public string Name { get; init; }
public decimal YearlySalary { get; init; }
public DateTime HiredDate{ get; init; }
}The init keyword is another new ingredient in C# 9. It lets you define a property that can only be set during object creation, either through a constructor (which the Employee class doesn’t have) or an object initializer, like this:
var theNewGuy = new Employee
{
Name = "Dave Bowman",
YearlySalary = 100000m,
HiredDate = DateTime.Now()
};After this point, there’s no way to change any of the properties of theNewGuy object.
Now, there are obviously plenty of reasons you might want to create a modified version of theNewGuy object in the course of an application. For example, maybe you want to give this employee a promotion and commit a new version of the record back to the database. To do that, you could construct a new Employee object and copy the important details over. But C# 9 has a more elegant solution using the with keyword. It gives you a concise way to create a new record using some of the details from another instance:
var promotedGuy = theNewGuy with { YearlySalary = 150000m }; Now, promotedGuy has the same values for Name and HiredDate as theNewGuy, but with a modified YearlySalary. Incidentally, the copying that with does is optimized for efficiency, because it can take advantage of the fact that the shared data will never change.
Records have some additional conveniences. They’re compared by value, not instance. That means if you have two records with the same data, they are deemed equal:
// Make a new record.
var yetAnotherGuy = promotedGuy with { YearlySalary = 100000m };// Even though these records are different instances (in object
// speak), they have all the same data, and so they are considered
// equal.
if (theNewGuy == yetAnotherGuy)
{
// You end up here.
}You could create an immutable class on your own, but it’s not quite as easy as you’d expect. You need to think about how records are initialized, compared, and copied. There’s a lot of boilerplate to write. But the record feature makes immutable objects feel like an effortless part of the language.
Expressions: Conditional logic, but 100% declarative
Another key idea in functional programming is that expressions are safer than control flow logic like loops. For example, let’s say you want to total up values in a collection. To keep a functional programmer happy, you’d use a reducer function that acts on every item in the collection. To make a functional programmer angry, you would iterate over the collection in a loop. (If you’re familiar with databases, this is the difference between a SQL query and a cursor that steps through a recordset. Functional programming prefers the query.)
The goal is to make code clearer, more testable, and — once again — immune to unexpected side effects. Who’s to say that code that’s iterating through your collection won’t change something? Who knows what really goes on in that loop?
In C#, there’s been a slow shift from imperative to declarative code with expressions. They first started to work their way into the language with LINQ in C# 3. Back then, many developers treated expressions as little more than an exotic feature for performing searches and querying data sources. But things changed in C# 7, 8, and now 9, with the steady refinement of switch expressions, a declarative alternative to conditional code.
Switch expressions hijack the old-fashioned switch branching keyword, but give it a completely new syntax. In fact, switch expressions have evolved to be so different from the classic switch statement that the shared keyword is little more than a potential point of confusion.
Instead of executing different blocks of conditional code, switch expressions use patterns to transform starting data into a final result. The best way to get an overall idea of how they work is to take a look at an example, like this CalculateToll() method adapted from Microsoft’s pattern matching tutorial:






