Wednesday, March 11, 2015

Roman Numeral Converter - Functional Programming Style

After watching Venkat Subramanian’s NDC session on Transforming your C# code to functional style I had the urge to try out some functional programming in C# of my own. I decided to re-tackle the Roman Numeral Converter problem which I had previously solved using a strategy pattern design.

My previous solution broke the converter problem down into a number of smaller converters which were each responsible for converting a numeric unit (ones, tens, hundreds and thousands) of the decimal value to its Roman numeral equivalent. I used strategies to represent each of these converters that could be enumerated through to complete the conversion process. You can find more details about my strategy pattern implementation in my blog post here.

Functional programming focuses on treating functions as variables which can be passed about as variables or added into collections that can be enumerated. This shift in programming paradigm can result in very powerful and interesting designs.

From watching Venkat’s session I knew that instead of having a number of strategies as part of my conversion process, I could have a number of algorithmic functions which when combined would perform the conversion process. These algorithms would be functions that could reside in an enumerable type that could be enumerated over to ensure each of the algorithms were applied to the decimal value.

The C# language has a number of types which can be utilized to help facilitate a more functional style of programming. The Action type allows a function which does not have a return type to be assigned to a variable, where a type of Func allows a function that returns a value to be assigned. There are many overloads for each of these types to cater for a range of input and output scenarios. For example, a function which accepts a string but returns an integer could be represented by the type Func<string, int> or Func<T in, T out>.

My functional implementation of the Roman numeral converter uses functions to convert a decimal unit into Roman numerals. These functions accept an integer and return a string so requires the following Func type to represent them:

Func<int, string> 

An implementation of one of the conversion algorithm functions looks like the following:
public static string Ones(int number)
 int onesToConvert = number % 10;

 string conversion = string.Empty;

 int i = 1;

 while (i <= onesToConvert)
  if (i == 4)
   conversion = "IV";
  else if (i == 5)
   conversion = "V";
  else if (i == 9)
   conversion = "IX";
   conversion += "I";

 return conversion;

I chose to create each of the conversion algorithms as a static method in a static class to help provide an easier API when accessing each of the algorithms:

By using a collection of Func<int, string> types, I was able to create a type which I could add each of my conversion algorithms to and enumerate in order to complete the conversion:
private List<Func<int, string>> _converters;

_converters = new List<Func<int, string>>

Once I had an enumerable of Func’s which represented each of the individual conversion algorithms, I enumerated through the converters collection, invoking each function with the number to convert and concatenate the returned conversion result:
string result = string.Empty;

_converters.ForEach(x => result += x(number));

return result;

It’s interesting to compare this approach to my previous solution which used the strategy pattern. The functional implementation feels a little more lightweight and free of ceremony as the strategy pattern required a different type to be created for each conversion strategy, each of which implemented an IConverter interface. The functional approach required none of this with the only constraint being the method signature of each of the conversion algorithms.

A question I debated was when is it preferable to use each approach? Referring to C# 5.0 in a Nutshell, it has some good general advice when the functional (or delegate) approach is more suitable:
  1. If the interface only defines a single method.
  2. Multicast capability is needed.
  3. The subscriber needs to implement the interface multiple times.
In the case of my strategy pattern implementation, points 1 and 3 are applicable. The IConverter interface only included a single method: string Convert(int number) and the IConverter interface needed to be implement multiple times as it was implemented in each of the conversion strategies.

You can find the complete source code, including unit tests, of my functional implementation of the Roman numeral converter here.

Watching Venkat’s NDC session and this brief dabble has peaked my interested in functional programming in general. After listening to a .NET Rocks podcast on the functional programming language Elixir, I could not resist starting to learning this and see what functional goodness I can pick up in 2015.

No comments:

Post a Comment