For many developers, the next step after learning a new language might be to port some existing code over to it, so that they can get a good feel for the differences between the two languages.
As we pointed out earlier, functional languages are very different from imperative languages, and so trying to do a direct port of imperative code to a functional language is often not possible, and even if a crude port is done successfully, the ported code will probably not be using the functional model to its best advantage.
Of course, F# is a multi-paradigm language, and includes support for object-oriented and imperative techniques, but even so, a direct port will generally not be the best way to write the corresponding F# code.
So, in this series, we’ll look at various approaches to porting existing C# code to F#.
If you recall the diagram from an earlier post, there are four key concepts that differentiate F# from C#.
And, as explained in that post and its sequels, these aspects are not just academic, but offer concrete benefits to you as a developer.
So I have divided the porting process into three levels of sophistication (for lack of a better term), which represent how well the ported code exploits these benefits.
At this first level, the F# code is a direct port (where possible) of the C# code. Classes and methods are used instead of modules and functions, and values are frequently mutated.
At the next level, the F# code has been refactored to be fully functional.
There are two different paths that can get you to this level.
The second option might seem clumsy, but for real code it will probably be both faster and more comfortable. Faster because you can use a tool such as Resharper to do the refactoring, and more comfortable because you are working in C# until the final port. This approach also makes it clear that the hard part is not the actual port from C# to F#, but the conversion of imperative code to functional code!
At this final level, not only is the code functional, but the design itself has been changed to exploit the power of algebraic data types (especially union types).
The domain will have been encoded into types such that illegal states are not even representable, and correctness is enforced at compile time. For a concrete demonstration of the power of this approach, see the shopping cart example in the “why use F#” series and the whole “Designing with types” series.
This level can only be done in F#, and is not really practical in C#.
Here is a diagram to help you visualize the various porting paths described above.
To see how these three levels work in practice, we’ll apply them to some worked examples:
But first, before we get started on the detailed examples, we’ll go back to basics and do some simple porting of some code snippets. That will be the topic of the next post.