Phase 1 of my mysterious proof of concept port from C# to F# is complete. And the good news is: the developer downstream has agreed to install the F# 2008 CTP and integrate my F# into the production version! He’s a good guy and always looking to stay with the cutting edge. So that he can continue to use the older data structures, I still have to write a small piece of glue code to do the conversion. (I wish I could say more, or post some code, but this is work I’m doing for someone else, who controls the business/publicity aspects of it.)
Here then, are some general observations after many hours of work and several attempts:
● One of the things I was looking at was the use of general .NET compatibility within an F# program vs. providing the compatibility via conversion at the interfaces.
● For my first attempt, I used general .NET-compatible data structures, objects, properties, and avoided things more specific to F#. That is, I used List<> instead of .NET lists, avoided tuples, used traditional class/file organization instead of a “module/let” organization, etc. I did use F# constructs like tail recursion, but I kept the overall class structure more traditional OO/imperative than functional.
● This “transliteration” approach worked OK. I was able to port the old stuff quickly and it resulted in fairly clean, easy-to-understand code. In particular, the number of lines of code was greatly reduced in comparison with the earlier C#/C++ versions. Where it did break down a bit was in maintenance and enhancement. As I went along, I was becoming more and more comfortable with F# and functional programming, and mixing those new skills and ideas with the OO/imperative stuff was creating tension in the code.
● So I re-wrote it, from scratch, into a more native F# version. I used lists instead of List<>, things like tuples where appropriate, and organized my code into a more F#-like namespace/module/let structure. I didn’t avoid OO/imperative constructs, but rather than allowing them to drive the design, I treated them as tools in the larger F# toolbox.
● Among other things, using F# constructs internally made me re-think the use of various data structures internally vs. at interfaces. I realized that I had spent a lot of time and effort designing hybrid structures that were usable in both capacities. However, these hybrids were ideal for neither. Thinking about an F# specific internal version helped me see those points where decoupling and conversion were superior to a “one-size-fits-all” approach.
● The result of all this “F#-ing” is code that is much cleaner and easier to maintain. There are even fewer lines of code than the already reduced number in the transliteration version. One key breakthrough was the use of computation expressions and other domain-specific language techniques. This really helped keep the code readable, composable, and extensible. The multi-paradigm nature of F# allowed me to mix declarative and imperative styles in a way that created simpler code than either style alone. Like oil and vinegar, the blend was more palatable than either alone.
● And if I ever do need to take it back to C#, the ideas I got from the F# port will make leave the code simpler and more extensible than the original. F#, I feel, is an excellent language for exploring ideas and designs, even if the final version ends up in some other language.
I’m so impressed, that I plan to port other portions of the project to F#, at least on a trial basis.