F# / C# Project Comparison

My first look into F# was in 2011 when I read Programming F#. I enjoyed working through that book and I realized that F# had opened the door to functional programming in .NET but at the time C# was vastly superior in both available information and developer experience. I set F# aside.

Recently I helped interview candidates applying for a principal software engineering position. One candidate (now employee) was very enthusiastic about functional programming in general and F# in particular. His enthusiasm rekindled my interest.

I started looking into the current state of F# and quickly discovered the previously mentioned gaps had closed significantly. Microsoft has made a commitment to F# as a first-class language and its projects are now beautifully interopable with the majority of other .NET project types. I decided I needed to re-explore F#.

F# Exploration Project

For my second dive into F# I decided to use Giraffe to recreate a web API I'd previously written in C#. Giraffe describes itself as "A native functional ASP.NET Core web framework for F# developers." My TrackIt exploratory application includes the ASP.NET Core Web API C# project TrackIt.Services.CoreWebApi. I used that project as the feature parity target for a new F# project. TrackIt.Services.GiraffeWebApi is the result.

Project Comparison

When complete the Giraffe implementation was a feature match for the one I'd previously created in C#. The following gives a sense of the differences in C# and F# code showing in-code handling of an HTTP DELETE to /datapoints/{id}

C#

public async Task<IActionResult> DeleteDataPointAsync(string id)  
{
    var result = await Manager.DeleteAsync(id);
    if (result.Success) { return Ok(result.Value); }
    else { return BadRequest(result); }
}

F#

let deleteDataPoint id =  
    fun (next : HttpFunc) (ctx : HttpContext) ->
        task {
            use manager = getDataPointManager configuration
            let! result = manager.DeleteAsync(id)
            if (result.Success) then return! jsonCamelCase true next ctx
            else return! (setStatusCode 400 >=> jsonCamelCase result) next ctx
        }

When I was done with the Griaffe project realized I had the opportunity to quantitatively compare feature parity code from two different languages. The metrics I decided upon for this comparison were file count, line count and significant character count.


Method

In each project only source code files were considered, i.e. .cs/.fs for the C#/F# projects respectively. Files were parsed for line count and significant characters. All blank lines and comments were excluded in parsing and significant character counts were found by trimming lines of spaces and removing tabs.

Results
Metric F# C# F#/C#
Files 6 7 0.86
Lines 219 312 0.70
Significant Characters 8376 9123 0.92
Discussion

The Files metric shows 14% fewer files in the F# project. However, the difference is a single file. For the comparison sample size no significant conclusions should be drawn from this discrepancy.

The Lines metric shows 30% fewer lines in the F# project. That's a significant number largely explained by C#'s use of the curly brace coupled with my personal coding standards, i.e. C# curly braces get their own lines.

The Significant Characters metric shows 8% fewer significant characters in the F# project. This difference seems a bit underwhelming but I'd argue that 8% less total code in a large application is very significant.


Final Thoughts

During this process I've found myself again drawn to the terse and tightly constrained nature of the F# language. The support the language now enjoys, especially with .NET Core 2.0, opens the door to a staggering number possibilities. I can say with 100% confidence it won't be another 6 years before I find myself working in an F# project.

References