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.

Switch statement case study

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.

Table-driven methods

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();

Simple pattern matching

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.

Summary

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.



blog comments powered by Disqus

Published

08 November 2015

Tags