Although bringing real pattern matching to C# in not my objective, I’ve tackled yet another syntax puzzle, trying to build a “sort-of” F#-ish pattern-matching-like syntax.
Disclaimer : as you can see from the quotes and italic, don’t expect me to build a fully-functional F# pattern matching in C#.
What is pattern matching ?
From the MSDN entry on Pattern matching :
Patterns are rules for transforming input data. They are used throughout the F# language to compare data with a logical structure or structures, decompose data into constituent parts, or extract information from data in various ways.
The point I want to focus on is in fact the F# match expression. The match expression is a powerful way to express conditions over data. It is often described as a “switch statement on steroids”. Its characteristics are the following :
- Just like a switch, it takes a value as its input,
- Unlike a switch, the value is not limited only to enums or built-in value types.
- It has a return type. If there is nothing to return, F# uses a special unit type, but there is no void as a return type neither a null value.
- If the values to return can be of different types depending on the input, Discriminated Unions (DUs) can be used as return types.
I’ll start with a simple F# sample, to show what a discriminated union is :
type Shape = | Point | Circle of float | Rectangle of float * float
In this sample, a type Shape is defined, that can be either a Point, a Circle or a Rectangle. Although the Point could be considered a simple enum value, the Circle and Rectangle cases, unlike enums, carry data. For instance :
let p = Point let c = Circle(5.0) let r = Rectangle(10.0, 20.0)
If we want to extract the data components of a particular union type, we use pattern-matching in the following way :
let Rectangle(w, h) = r
This expression binds the value of w and h to the data elements of the r Rectangle defined earlier. But my purpose in the blog series is to introduce in C# a syntax that would look like a match expression, which in F# is the following :
let getArea s = match s with | Point -> 0.0 | Circle(r) -> r * r * System.Math.PI | Rectangle(w, h) -> w * h
In this attempt, I first consider matching simple values (which has no practical interest of any sort), then matching tuples, and I’ll finish with matching against some sort of Discriminated Union, although DUs are not a native C# construct.
First, let’s consider what we want to build. From the sample above, we can see that what we get from the match expression is a “getArea” that takes an instance of the union type as input, and gives a float as output. As this is just the definition of a function, it looks like we’re trying to build a construct that generates instances of the generic Func<TInput, TOutput> delegate.
In F#, match expressions can also be used to perform actions, and in this case will return the special unit type. To perform actions in C# we have the Action<T> delegate type. In this series I’ll consider only the functions, but actions could be built exactly the same way.
In the next part, I’ll show my first naive attempt to deal with simple values, before targeting any tuples or discriminated union types.