5-Dec

Functional

Functional ... C#?

4 min read

·

By Runar Ovesen Hjerpbakk

·

December 5, 2020

I really love C#. It's by far my favorite programming language. And who wouldn't agree? I mean with a piffy, roll of the tounge Wikipedia description like this, what is not to love?

"C# is a general-purpose, multi-paradigm programming language encompassing static typing, strong typing, lexically scoped, imperative, declarative, functional, generic, object-oriented (class-based), and component-oriented programming disciplines."

"Yeah, yeah", I hear you say dear reader. "I know C# is great to use if you want to write better Java apps, even so, this is the Bekk Functional advent calendar. What are you rambling about?"

I know you love your pure, scalable functions in Haskell and your fancy web pages written in Elm, but did you spot the magic word in the description above? It said:

"... functional ..."

Yes! As the late Sean Connery said so excitedly:

The day is mine!

C# is not only useful on the backend, while writing iOS and Android apps, replacing JS in the browser or as a scripting language, it also becomes more and more functional-friendly with every new language version!

However, what does it mean to be a functional-friendly programming language?

Functional advantages

Enrico Buonanno argues in his book Functional Programming in C#: How to write better C# code that we should care about functional programming because it gives us the following:

  • Power: We can get more done with less code. Functional programming raises the level of abstraction, allowing us to write high-level code while freeing us from low-level technicalities that add complexity but no value.
  • Safety: A program written in the imperative style may work well in a single-threaded implementation but due to the mutable state that is in its nature, cause all sorts of bugs when concurrency comes in. Code in the functional style might offer better guarantees in concurrent scenarios.
  • Clarity: We spend more time maintaining and consuming existing code than writing new code, so it’s important that our code be clear and intention-revealing. As we learn to think functionally, achieving this clarity will become more natural.

C# supports a lot of functional building blocks, such as function delegates, higher order functions, expressions instead of statements, method chaining, extension methods, yield, LINQ, tuples and local functions. Despite all that, the core functional tenet of immutability of data has always been a C# pain point. Until now.

Immutability in C#

C# second greatest error[^error] was to one-up Java and introduce the concept of properties as a first class language feature with the poorest defaults of all time: the default property syntax made it all too easy to create classes with mutable properties. The deed is as simple as:

public class Person
{
    public uint Age { get; set; }
}
Listen to the wolf

Alas, premature aging can take its toll and we should've listened to the dog, hound, wolf or whatever, and make the age property immutable. It came with a cost however, amounting to a lot more work and boilerplate code. I mean, I got bored by just writing out this small example from the past:

public class Person
{
    private readonly uint age;

    public Person(uint age)
    {
        this.age = age;
    }

    public uint Age { return age; }
}

C# 6 made the pit of success a bit deeper by introducing read-only properties using only a get accessor:

public class Person
{
    public Person(uint age)
    {
        Age = age;
    }

    public uint Age { get; }
}

C# 7.2 made declaring our intent more explicit by allowing a struct to be marked as readonly:

public readonly struct Person
{
    public Person(uint age)
    {
        Age = age;
    }

    public uint Age { get; }
}

This is all well and good, yet consider when our class or struct has multiple readonly properties and we need to construct a new copy with an updated value in one or more of them. My head hurts just by thinking about it. The boilerplate has tested my Christmas spirit, why must we endure this error prone, manual waste? A better way must exist!

Records in C# 9

And finally this year, at the end of the worst year ever, it does! Microsoft bestowed .Net 5 and C# 9 upon us, giving us my most requested feature: language support for immutable data types, Record types!

public record Person(uint Age);
// No, really, this the complete example!

A record is immutable in that none of the properties can be modified once it's been created. When we define a record type, the compiler provides several useful methods for us:

  • Methods for value-based equality comparisons
  • Override for GetHashCode()
  • Copy and Clone members
  • PrintMembers and ToString()

Seems like we're finally winning the war against boilerplate, and if we need to create a copy with an updated value, we can now easily use the with keyword:

var me = new Person(37);
Person meAYearOlder = me with { Age = 38 };

With such quality functional features in C# like immutable records, not even time's inevitable flowing towards my 40th birthday and usage of 24(!) year old memes can break this aging developer's Christmas spirit!

[^error]: C#'s greatest error was to introduce the concept of null and not fixing it until C# 8.