Enrico Buonanno


Is C#7 starting to look like a functional language?

This article is excerpted from Chapter 1 of: Functional Programming in C#

Many programmers make a tacit assumption that "you can only do functional programming (FP) in a functional language", and that, given that C# is an object-oriented language, it’s not even worth making the attempt to program functionally in C#.

Of course, this is a superficial take. If you have more in-depth knowledge of C# and its evolution, you probably know that C# is a multi-paradigm language (just as F# is), and that, although it started as a mostly imperative and object-oriented language, many functional features have been added, and are still being added, with every successive release.

So, this begs the question: how well does C# support programming in a functional style today? To answer this, let me first clarify what I mean by functional programming; namely, a programming paradigm that:

  1. emphasizes the use of functions, and
  2. avoids state mutation

To support this style of programming, a language must:

  1. support functions as 1st-class values; that is, it must be possible to treat functions just as any other value; specifically, you can use functions as arguments to or return values of other functions, or store functions
    in collections
  2. discourage in-place updates (or indeed make them impossible): variables, objects, and data structures should be immutable by default, and it should be easy to create modified versions of an object
  3. automatically manage memory: because we’re creating such modified copies rather than mutating data in-place, we end up creating more objects; this is impractical in a language without automatic memory management

With this in mind, we’re ready to broach the question:

How functional a language is C#?

Well… let’s see.

1) Functions are indeed 1st-class values in C#. For example, 
consider this code:

Func<int, int> triple = x => x * 3;
var range = Enumerable.Range(1, 3);
var triples = range.Select(triple);
triples // => [3, 6, 9]

This demonstrates functions as 1st-class values, since we can assign our 
multiply-by-3 function to the variable triple, and give it as an argument to Select.

In fact, C# had support for functions as 1st-class values from the earliest version of the language, through the Delegate type, and the subsequent introduction of lambda expressions made the syntactic support even better.

There are some quirks and limitations when it comes, for example, to type inference (especially when we want to pass multi-argument functions as arguments to another function); I discuss these in Chapter 8. But, overall, the support for functions as 1st-class values is pretty good.

2) Ideally, we would also like the language to discourage in-place updates. Here is C#’s greatest shortcoming: everything is mutable by default, and the programmer has to do a substantial amount of effort to achieve immutability. Fields and variables are all mutable by default, and must explicitly be marked readonly to prevent mutation. (Compare this to F#, where variables are immutable by default, and must explicitly be marked mutable to allow mutation.)

What about types? While there are a few immutable types in the framework,
such as string and DateTime, language support for user-defined immutable types is poor (although, as you’ll see next, it has improved in C#6 and is likely to further improve in future versions). Finally, collections in the framework are mutable, but a solid library of immutable collections is available.

3) On the other hand, C# does satisfy the more important requirement of automatic memory management. This means that, although it doesn’t encourage a programming model that avoids in-place updates, it does support it thanks to garbage collection.

In summary, C# has very good support for some functional techniques,
but not others. In its evolution, it has improved, and will continue to
improve its support for functional techniques.

Next, we’ll review some language features from past, present, and upcoming versions of C# that are particularly relevant to FP.

The functional nature of LINQ

When C# 3 was released, along with the .NET framework 3.5, it included a
host of features inspired by functional languages: this consisted of the LINQ library (System.Linq), and some new language features enabling or enhancing what you could do with LINQ, such as extension methods and expression trees.

LINQ offers implementations for many common operations on lists (or, more
generally, on “sequences”, asIEnumerables should technically be
called); the most common of which are mapping, sorting, and filtering.
Here’s an example combining all three:

Enumerable.Range(1, 100).
Where(i => i % 20 == 0).
OrderBy(i => -i).
Select(i => $”{i}%”)
// => [“100%”, “80%”, “60%”, “40%”, “20%”]

Notice how Where, OrderBy, and Select take other functions as arguments, and do not mutate the given IEnumerable, but return a new IEnumerable instead, illustrating both the tenets of FP I mentioned above.

LINQ facilitates querying not only objects in memory (LINQ to Objects),
but various other data sources, like SQL tables and XML data. C# programmers have embraced LINQ as the standard toolset for working
especially with lists and relational data (accounting for a substantial amount of a typical codebase). On the up side, this means that you already have some sense of what a functional library’s API feels like.

On the other hand, when working with other types, C# programmers generally stick to the imperative style of using control flow statements to express the program’s intended behaviour. As a result, most C# codebases I’ve seen are a patchwork of functional style (when working with IEnumerable-s and IQueryable-s) and imperative style (everything else).

What this means is that,

while C# programmers are aware of the benefits of using a functional library such as LINQ, they have not had enough exposure to the design principles behind LINQ to leverage those techniques in their own designs.

This is something this book aims to address.

Functional features in C#6 and C#7

While not as revolutionary as C#3, C#6 and C#7 bring many smaller language features that, taken together, provide a much better experience and more idiomatic syntax for coding functionally.

NOTE: Most of the features in C#6 and C#7 introduce better syntax, not new functionality. So, if you’re using an older version of C# you can still apply all of the techniques shown in this book (with a bit of extra typing). However, these features significantly improve readability, making it more attractive to program in a functional style.

Let’s see these features in action in the listing below, and then discuss why they’re relevant to FP.

Listing 1. C#6 and C#7 features relevant for FP

using static System.Math;                          <1>
public class Circle
public Circle(double radius)
=> Radius = radius; <2>
   public double Radius { get; }                   <2>
   public double Circumference                     <3>
=> PI * 2 * Radius; <3>

public double Area
double Square(double d) => Pow(d, 2); <4>
return PI * Square(Radius);
   public (double Circumference, double Area) Stats   <5>
=> (Circumference, Area);
  1. using static enables unqualified access to the static members of System.Math, like PI and Pow below
  2. a getter-only auto-property can only be set in the constructor
  3. an expression-bodied property
  4. a local function is a method declared within another method
  5. C#7 tuple syntax allows for member names

Importing static members with "using static"

The using static statement in C#6 allows us to “import” the static members of a class (in this example, the System.Math class). As a result, in our example we can invoke the PI and Pow members of Math without further qualification.

using static System.Math;
public double Circumference
=> PI * 2 * Radius;

Why is this important? In FP, we prioritize functions whose behaviour only relies on their input arguments, because we can reason about and test these functions in isolation (contrast this with instance methods, whose implementation relies on instance members). These functions are implemented as static methods in C#, so a functional library in C# will consist mainly of static methods.

using static allows us to more easily consume such libraries, and while overuse can lead to namespace pollution, a reasonable use can make for clean, readable code.

Easier immutable types with getter-only auto-properties

When you declare a getter-only auto-property, such as Radius, the compiler implicitly declares a readonly backing field. As a result, these properties can only be assigned a value in the constructor or inline.

public Circle(double radius) 
=> Radius = radius;
public double Radius { get; }

Getter-only auto-properties in C#6 facilitate the definition of immutable types. The Circle class demonstrates this: it only has one field (the backing field of Radius), which is readonly; so, once created, a Circle can never change.

More concise functions with expression-bodied members

The Circumference property is declared with an “expression body” introduced with =>, rather than the usual “statement body” in { }. Notice how much more concise this is compared to the Area property!

public double Circumference
=> PI * 2 * Radius;

In FP, we tend to write lots of simple functions, many of them one-liners, and then compose them into more complex workflows. Expression-bodied methods allow us to do this with minimal syntactic noise. This is particularly evident when we want to write a function that returns a function — something we’ll do a lot in the book.

The expression-bodied syntax was introduced in C#6 for methods and properties, and generalized in C#7 to also apply to constructors, destructors, getters, and setters.

Local functions

Writing lots of simple functions means that often functions are only called from one location, and C#7 allows us to make this explicit by declaring methods within the scope of a method; for instance, the Square method is declared within the scope of the Area getter.

double Square(double d) => Pow(d, 2);
return PI * Square(Radius);

Better syntax for tuples

This is probably the most important feature of C#7. It allows us to easily create and consume tuples, and, most importantly, assign meaningful names to their elements. For example, the Stats property returns a tuple of type (double, double), but additionally specifies meaningful names by which its elements can be accessed.

public (double Circumference, double Area) Stats
=> (Circumference, Area);

The reason why tuples are important in FP again comes down to the tendency to break tasks down into very small functions. We may end up with a data type whose only purpose is to capture the information that is returned by one function, and expected as input by another function. It’s impractical to define dedicated types for such structures, which do not correspond to meaningful domain abstractions, and that’s where tuples come in.

A more functional future for C#?

As I was writing the first draft of this chapter, in early 2016, development of C#7 was in its early days, and it was interesting to see that all the features for which the language team had identified “strong interest” were features normally associated with functional languages. They included:

  • Record types (boilerplate-free immutable types)
  • Algebraic data types (a powerful addition to the type system)
  • Pattern matching (similar to a `switch` statement that works on the “shape” of the data, for example its type, rather than just the value)
  • Better syntax for tuples

On one hand, it was disappointing that only the last item could be delivered. A limited implementation of pattern matching is also being shipped, but still a far cry from the kind of pattern matching available in functional languages, and generally inadequate for the way we’d like to use pattern matching when programming functionally.

On the other hand, these features are still on the table for future versions, and work has been done on the respective proposals. This means we’re likely to see record types and a more complete implementation of pattern matching in future versions of C#.

So, C# is poised to continue in its evolution as a multi-paradigm language with an increasingly strong functional component. What you learn in this book will give you a good foundation to keep up with the evolution of the language and the industry, and a good understanding of the concepts
and motivations behind future versions of the language.

More by Enrico Buonanno

Topics of interest

More Related Stories