I once wrote about the difference that your choice of language can make [now lost due to a traumatic blogging platform change]. My analysis was based on two code-bases written in different languages on the same platform implementing similar applications.
Having just gone (as of 14/02/2014) code-complete(ish) on my latest project it is time to revisit this topic. Now I can base my analysis on two code-bases written in different languages on the same platform implementing the same application.
In this comparison two different teams worked on the two diffent solutions.
I know language comparisons are frought with issues. This is the best comparison I have to-date between F# and another lanaguage being used by real teams on real projects. For all its faults I find the results of the comparison quite compelling.
I presented these results at Software2014 and to the NNUG. The slide pack for the presentation can be found here: http://www.slideshare.net/simontcousins/time-for-functions
The problem was to develop an application to evaluate the revenue due from Balancing Services contracts. Balancing Services are the services provided by generation companies to the Transmission Service Operator (National Grid in the UK) to assist in maintaining the stability and security of the electricity supply. Some of these contracts are a tad complicated.
The contracts need to be evaluated in near-real-time based on metered data to provide control room staff with information to make power-plant scheduling decisions. The contracts also need to be evalated periodically on settlement data to reconcile contract invoices.
There was an existing solution implemented in C# which had a number of issues:
- not all of the contracts had been implemented fully as they were deemed to be too complex to implement
- it was very hard to maintain the large code base as changes were introduced
- it was impossible to test without live data
- there was low confidence in the numbers it produced
- it often failed to evaluate the contracts in near-real-time
To fix the above it would be nice if we had a language that:
- had a strong type system that worked with the developer
- had a REPL to facilitate exploratory programming
- good abstractions for asynchronous I/O
- low ceremony data abstractions
- control flow abstractions for handling large data sets
- computation abstractions for cleanly expressing algorithms
- made testing easy
Sounds like another job for F#!
But where do you find F# developers?
Fortunately I had one sitting next to me: Colin. Colin and I had learnt F# together by using it on real projects since 2008.
We needed another team member though. So we hired a good developer, Andy. Andy had no previous F# experience so I lent him my copy of "F# for Scientists" by Jon Harrop. Still the slimmest F# book I possess and my favourite.
I reckon that if you have .NET experience you can pick up enough F# in a week to start cutting production code. In a month you will have stopped mutating state for a living and have become a functional developer.
With three F# devs, we were good to go!
Design Principle: stay safe, stay functional.
Although F# is a multi-paradigm language the one design principle we adopted was to stay as purely functionally as much as possible. In particular, we have an immutable domain model and persistence store. Pure functions are used to work with this domain model.
Step One is to write a proof of concept. To do this we needed to think which meant we had to get out of the office. To do this we hired a room for a week at our local university.
There are many xDDs but the only one I have found worthwhile is DDD. So we read the contracts and came up with our ubiquitous language for the project:
[<Measure>] type min [<Measure>] type hh [<Measure>] type h [<Measure>] type MW type Interval<'t> = 't * 't type Point<'x,'y> = 'x * 'y type Segment<'x,'y> = Point<'x,'y> * Point<'x,'y> type Line<'x,'y> = Segment<'x,'y> list
There was a bit more to the language but the essence is captured in the above. It is interesting to note that we found the concepts of units of measure, interval, point, segment and line to be fundamental to the problem at hand. We did not find any of these concepts in the existing solution.
Functional Programming: No abstraction is too small. Any abstraction worth having is composable. Reuse is pervasive.
Object-Oriented Programming: all too often the abstractions are coarse and not composable. Reuse is limited.
With two days left Colin fired up the REPL and developed a proof-of-concept strawman for the contract evaluation pipeline. The next two days where spent playing with this code and seeing what worked and what didn't.
By the end of the week:
- No UML was produced.
- A ubiquitous language had been defined.
- We had an executable proof-of-concept to run with.
Now back to the office to delete code from the proof-of-concept until production ready.
Colin started work on another project so Andy and I started implementing the contracts in June. Two F# devs.
Then Colin left to seek his fortune in the big city. With the tears streaming down our faces following Colin's departure I had to pick some work Colin was doing and Andy carried on his own. One F# dev.
After a month or two I rejoined the project. Two F# devs.
In January recruited another dev with no F# experience from our offshore team. After a week or so back to three F# devs.
Code-complete in February.
Brace yourself. Here come the numbers.
Some More Observations
Logging Statement LOC
I see a lot of logging statements in code-bases when the developers don't understand what their code is doing.
Exception Handling LOC
I see a lot of exception handling in code-bases where developers have lost confidence in their ability to write robust code.
Test Code Ratio
One positive side-effect of not writing so much application code is that there is more time to write tests.
Useful Code Ratio
Almost half of the C# code is noise. It is hard to think with noise.
The C# project took five years and peaked at ~8 devs. It never fully implemented all of the contracts.
The F# project took less than a year and peaked at three devs (only one had prior experience with F#). All of the contracts were fully implemented.
Initial test results look promising:
But Andy wants a faster compiler/machine:
In My Humble Opinion
I can fit the whole F# solution in the blank lines of the C# solution with 7,000 lines to spare.
WTF C#! OMG F#!
I have spent most of my life writing buggy, bloated software that was impossible to reason about. What was I thinking?
Thanks goes to...
Don Syme and all the F# team at Microsoft for delivering a world class functional programming language to enterprise programmers.
The F# community for being so friendly and supportive. In particular Philip Trelford and Tomas Petricek for so tirelessly spreading the message.
All those OSSers that work on FAKE, FSharpx, FSharp.Data, FSharp.Charting, FSUnit, FSharpBinding, FSharp.Formatting and FsCheck to make the awesome even more awesome.
Kit Eason for going militant. I like the cut of your jib (and your noise analysis script, written in F# naturally).
Special thanks to Colin Bull for all those inspirational chats during compilations and sticking it to the man.
Special thanks to Andy Calderbank for being such a good F# guinea pig and willing to jump in at the deep-end.
Finally, if you want to learn F# there is no better place than Scott's: http://fsharpforfunandprofit.com/