Psuedocode-Driven Development with F#

... because there's nothing better than *DD acronyms, right?

Tl;dr - F# lets you write pseudocode.
Once that pseudocode compiles, your program is probably right (or very close to it).
(This is just another sales pitch for the |> operator)

I gave a F# hands-on lab session to a local user group a few weeks ago.
The lab was heavily based on the lovely "F# Koans" program, with a rewritten test-runner.
The Koans works by learning very small language features one at a time, while making unit tests pass.

You can find my rendition of the Koans here.

While learning small language features one at a time is important in making sure no one gets scared, it's extremely important to have an overarching example of why F# is great, and how we think in F#.

This is where this test comes into play:

Given a list of comma-separated strings representing stock data, the task is to find the date in which there is the largest variance in opening/closing prices.

In thinking about this problem, I understand that I want to write a function that:
given a list of strings, returns a 'date' (actually a string representation of this date, to make the test pass.)

Any developer should be able to look at the following code and figure out what the intention is

let getDateForHighestVarianceInStock (data : string list) : string =
	()

let result = getDateForHighestVarianceInStock stockData

AssertEquality "2012-03-13" result

We've essentially written pseudo-code.
Code like (string list) are extremely simple to read coming from any language .
Further the : string at the end of our function declaration makes it pretty explicit that we want to return a string from our function.

Currently our code doesn't compile, and that's great!

My statement at this point of the talk was (half-serious) "If we can make this code compile, the test will pass" as I've often found with F#.

At this point, we can write some very obvious pseudo-code:

let getDateForHighestVarianceInStock (data: string list) : string = 
    data
    |> skipHeaderRow
    |> projectToAFancyType
    |> orderByDifferenceDescending
    |> takeHead
    |> getTheDateFromTheRecord

As a software developer with much experience, the flow of data in this function should be somewhat obvious.
All we have to do now is fill in the functions, and this is the point I want to bring home.

Many times, the fluent syntax of F# allows us to write pseudo-code in this fashion, for doing complicated algorithmic work, and come out on the other side with a clean solution.

Now that we've written our pseudo-code we have to fill in our test with some 'real' functions.
The first thing we'll do is create some helper functions.

let splitCommas (x:string) = x.Split([|','|])
let parseDouble str = System.Double.Parse(str)

There have been given to us in the problem statement.

Next, we need a 'type' to hold our data in, once it's been parsed out.

type Row = { Date: string; Close: float; Open: float }

Again, F#'s syntax makes it very nice for a new F# developer to guess what's going on.

Now that we've done some pre-work, we can start making our code compile!

Remember, these are the functions we need to fill in:

|> skipHeaderRow
|> projectToAFancyType
|> orderByDifferenceDescending
|> takeHead
|> getTheDateFromTheRecord

So let's go one by one.

let skipHeaderRow = Seq.skip 1

This is simply a helper around a built-in F# function that skips the header row.
Both the name and the implementation should be pretty obvious to a new F# developer.

let projectToTheFancyType rows = 
    rows
    |> Seq.map splitCommas
    |> Seq.map(fun z -> { Date = z.[0]; Close = parseDouble z.[4]; Open = parseDouble z.[1] })

Given a list of strings (string list), we want to create a (* list) of some other type, namely the Row type we defined before.
To do that, we will split each of the rows by commas, and then project them into the appropriate type.

let orderByDifferenceDescending rows =
    rows |> Seq.sortBy(fun (z:Row) -> z.Close - z.Open |> abs |> (*) -1.0)

Next, we want to sort the rows, this being likely the most complicated part.
if we wanted, we could create an inner function for the 'mathy' part of the algorithm, but

(z.Close - z.Open) |> abs |> (*) -1.0

should illustrate fairly cleanly that we want to take the difference and sort in a descending fashion (largest difference first)

let takeHead rows = rows |> Seq.head

Similar to 'skipHeaderRow', this is simply an alias around a built-in F# function!

 let getTheDateFromTheRecord row = row.Date

Finally, given a row, return the 'Date' part of this type.

When we put this all together we get

let stockData =
    [ "Date,Open,High,Low,Close,Volume,Adj Close";
     	//...
      "2012-02-29,31.89,32.00,31.61,31.74,59323600,31.74"; ]


type Row = { Date: string; Close: float; Open: float }




let splitCommas (x:string) = x.Split([|','|])
let parseDouble str = System.Double.Parse(str)

let skipHeaderRow = Seq.skip 1

let projectToTheFancyType rows = 
    rows
    |> Seq.map splitCommas
    |> Seq.map(fun z -> { Date = z.[0]; Close = parseDouble z.[4]; Open = parseDouble z.[1] })
    

let orderByDifferenceDescending rows =
    rows
    |> Seq.sortBy(fun (z:Row) -> z.Close - z.Open |> abs |> (*) -1.0)

let takeHead rows = rows |> Seq.head

let getTheDateFromTheRecord row = row.Date

let result = 
    stockData
    |> skipHeaderRow
    |> projectToTheFancyType
    |> orderByDifferenceDescending
    |> takeHead
    |> getTheDateFromTheRecord

AssertEquality "2012-03-13" result

Most importantly, our pseudo-code is still in tact, and we can unit-test any part of its internals individually.