Adventures in F#--Sweet Test-First Kung Fu

Jomo Fisher--Up until now, I've been avoiding using F# with the VS IDE. I've been using notepad.exe and fsc.exe because I wanted to build my own expectation for what the experience should be before I experienced what it actually was. I can tell you that I didn't expect the sweet experience of using the F# interactive window.

I've written in the past about Test Driven Development. Regardless of language, I really like the process of iteratively writing code and unittests tests for that code. For very small projects--simple algorithms and classes--I've found the process of getting going very tedious compared to the amount of actual code I want to write. In order to get started, you need a new project and solution for your code, a separate project for your tests and you need to reference your code project from your test project. All this means friction and tends to discourage firing up VS and freely inventing for very small projects.

Enter the F# Interactive window. Once you've installed F# and checked the Add-in box under Tools\Add-in Manager you'll see the F# interactive window. You can type code into this window and it will execute immediately. Importantly, you can select some text from your current source file and press alt-enter to execute it.

So here's my new process for these tiny one-off projects:

1) Ctrl-N to create a new text file (rename to mycode.fs)

2) Type in the skeleton of the function

3) Select function and press alt-enter

4) Write test, select it and press alt-enter (see failure)

5) Fix function to make test pass and goto 3

Notice that there's not a project or even a solution involved here--its just developer writing code. It's a really sweet experience. I only wish F# would install over C# Express 2008 since that's what I tend to use at home.

I like to show code in my posts when possible and I want to give you a visual idea of what I'm talking about. Here's a simple function I wrote--with tests--that takes a string in the form of A.B.C.D or vA.B.C.D and returns a tuple of 16-bit ints with values (A,B,C,D). The tests are at the end.

 #light
let ParseVersion s = 
    // Build a list with each element of the version in reverse order.
    let rec parse(s:string,pos,count,result) = 
        let versionSize = 4
        if pos >= 0 then 
            if s.[pos] = 'v' && pos = 0 then parse(s,1,0,[])
            else 
                let dotPos = s.IndexOf(".", pos)
                if dotPos = -1 then parse(s, -1, count+1, int_of_string(s.Substring(pos))::result)
                else parse(s, dotPos+1, count+1, int_of_string(s.Substring(pos, dotPos-pos))::result)
        elif count < versionSize then parse(s, pos, count+1, 0::result) 
        else result
        
    let l = parse(s,0,0,[])
    (uint16(List.nth l 3), uint16(List.nth l 2), uint16(List.nth l 1), uint16(List.nth l 0))
    
ParseVersion "2.0.1"
ParseVersion "2.0"
ParseVersion "v2.0"
ParseVersion "v1.2.3.4"
ParseVersion "v1"
ParseVersion "1.2.3.4"

(Notice how I made it tail-recursive to take advantage of F#'s tail-call optimization?)

I wrote this using more-or-less test-first principles and I was very happy with the flow of the process.

This posting is provided "AS IS" with no warranties, and confers no rights.