Nowadays, functional languages are becoming very popular. Few years ago we thought that the object orientation would be the answer for complexity in systems. But now, there is a belief that functional languages are the new solution. We will see what is gonna be the “thing” in the next years.
Lately, I had an occasion to apply one of feature from functional languages i.e pattern matching to code written in C#. But let’s start from the beginning.
Couple days ago, I encountered a piece of code which you can find below. It checks which hint is passed to the method. Then, it returns appropriate provider.
public IHintProvider GetHintProvider(string hint)
{
switch (hint)
{
case HintOne:
return new HintOneProvider();
case HintTwo:
return new HintTwoProvider();
case HintThree:
return new HintThreeProvider();
case HintFour:
return new HintFourProvider();
}
return new NullHintProvider();
}
It looks like a procedural code and I felt bad about the code, especially that I made it and probably forgot to refactor it. Nevertheless, it is not as worst as it could be, because for example it could have used the if
and else if
statements.
I started to look for better alternatives, whether I can refactor this slightly to reduce the switch statement noise. This led me to a table-driven method. I have known it before from Code Complete book. Basically it means, that sometimes we can precompute answers for given paths and store it in a table (or in our case in dictionary). Then, given an argument we just do lookup and return stored value. Owing to using this technique, we can now refactor GetHintProvider
method:
private readonly IDictionary<string, IHintProvider> providers = new Dictionary<string, IHintProvider>
{
{HintOne, new HintOneProvider()},
{HintTwo, new HintTwoProvider()},
{HintThree, new HintThreeProvider()},
{HintFour, new HintFourProvider()}
};
public IHintProvider GetHintProvider(string hint) =>
providers.ContainsKey(hint) ? providers[hint] : new NullHintProvider();
But, as you can see the syntax of creating dictionary mimics somewhat the pattern matching feature from functional languages. I have been wondering if we are able to use this construction to create a simple pattern matching functionality in C#
. It turns out, that C#
compiler in some situations uses kinda duck-typing. If a type implements IEnumerable
, we can use collection initializer while creating object instance of that type. Instances passed in collection initializer compiler translates to corresponding Add
methods with matching signature. Given that, we can create Match
class that stores patterns and associates code with them to execute when pattern matches. With that, we can write GetHintProvider
method as following:
public IHintProvider GetHintProvider(string hint) =>
new MatchFunc<string, IHintProvider>
{
{HintOne, s => new HintOneProvider()},
{HintTwo, s => new HintTwoProvider()},
{HintThree, s => new HintThreeProvider()},
{HintFour, s => new HintFourProvider()},
{Match.Default, s => new NullHintProvider() }
}.Func(hint).Single();
Now, the code looks nice to me and it is more declarative than the original example.
If you are interested in, you can check code for MatchFunc
class here.
Being declarative, expressing intent of code is an advantage of functional languages, that is presented as opposite to imperative code. Actually, I think code is easier to understand and to maintain. I’m curios whether C#
language engineers have thought that its collection initializers syntactic sugar could be used in such a way. If not, it is interesting how the solution to problem in software field can be used in not foreseen ways later.