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:
To support this style of programming, a language must:
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 functionsin collections
With this in mind, we’re ready to broach the question:
Well… let’s see.
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.
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.
In summary, C# has very good support for some functional techniques,but not others. In its evolution, it has improved, and will continue toimprove 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.
When C# 3 was released, along with the .NET framework 3.5, it included ahost 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, moregenerally, on “sequences”, asIEnumerable
s should technically becalled); 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 workingespecially 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.
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{get{double Square(double d) => Pow(d, 2); <4>return PI * Square(Radius);}}
public (double Circumference, double Area) Stats <5>=> (Circumference, Area);}
using static
enables unqualified access to the static members of System.Math
, like PI
and Pow belowusing 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.
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.
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.
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.
get{double Square(double d) => Pow(d, 2);return PI * Square(Radius);}
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.
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:
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 conceptsand motivations behind future versions of the language.
For more information (and a discount code!) on Functional Programming in C# download the free first chapter or see this slideshare presentation.
Manning | Functional Programming in C#_Functional programming is a way of thinking about programs that emphasizes functions, while avoiding state mutation. It…_www.manning.com