The combination of a functional language and an interactive interpreter has helped me think differently about solving problems. The functional language makes me write expressions - code that returns an answer; and the interactive window lets me test those expressions. Since it tends to be easier to test small things in isolation, I naturally end up with many small functions that work together to solve larger problems. I like to call these small functions Building Blocks.

Here’s an example of the process using the coding problem from Cleveland’s first F# User Group meeting.

Step 1: Break the problem down

1
2
3
4
//Given a directory
// 1. Get the files of that directory
// 2. Get the files in subdirectories of the directory
// 3. Print out all the file names

Easy enough. Just a few comments to guide my way. When I initially do this I express the objectives at whatever level of detail I can comprehend at that time. As I begin coding I can sometimes break an objective down into smaller objectives. Keep this in mind while you are coding. It will have an impact in your problem solving skills and in the shape of your finished code.

Step 2: Start implementing

1
2
3
4
5
6
7
8
//Given a directory
let directory = "C:\Temp"
// 1. Get the files of that directory
open System.IO
let files = Directory.EnumerateFiles(directory)
// 2. Get the files in subdirectories of the directory
// 3. Print out all the file names
Seq.iter (fun f -> ignore(printfn "%s" f)) files

Do one thing at a time and just enough to complete your objective. Sound familiar?

All I did here was just enough to make sure that I could find all of the files in a given directory. I used the F# Interactive window to print out the files in order to ensure that my code was doing what I expected. Since the printing code seemed simple enough and I had evidence that it worked from using FSI I went ahead and kept that code too.

Step 3: Set up the building blocks

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
open System.IO

// 1. Get the files of that directory
let getFiles directory =
    Directory.EnumerateFiles(directory)

// 2. Get the files in subdirectories of the directory

// 3. Print out all the file names
let printFiles files =
    Seq.iter (fun f -> ignore(printfn "%s" f)) files

let files = getFiles "C:\Temp"
printFiles files

You might call this a refactoring. I simply extracted the two things my code is doing into their own functions; and called them. You can start to see a program taking shape!

Step 4: Do the recursion

WARNING! There is an explanation of the code beneath the example. If you are new to F# then you may want to gloss over the code and then follow along. In the next code example things will become much neater.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
open System.IO

//Get the files of the specified directory and its subdirectories
let getFiles directory =
    let rec getFilesHelper files dir =
        let subDirectories = Directory.EnumerateDirectories(dir)
        Seq.append (Directory.EnumerateFiles(dir)) (Seq.collect (fun s -> getFilesHelper files s) subDirectories)

    getFilesHelper Seq.empty directory

//Print out all the file names
let printFiles files =
    Seq.iter (fun f -> ignore(printfn "%s" f)) files

let files = getFiles "C:\Temp"
printFiles files

We all know that file systems are tree structures and you can get all the files from a node by using recursion1. What you see here is just a first pass at getting the code working properly. There are two important things to notice at this stage:

  • The way the recursion works.  I first get the subdirectories and then use Seq.collect to call getFilesHelper for each subdirectory.  The result of getFilesHelper is a sequence of file names so each folder has the files appended into one large sequence.
  • The use of a “helper” function.  The getFilesHelper function takes an argument that collects the results of each of the recursive calls.  By creating this function as a helper inside the main getFiles function we can spare the calling code those details; and avoid the smell of an output parameter.

Step 5: Clean up

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
let getFiles directory =
    let rec getFilesHelper files dir =
        Directory.EnumerateDirectories(dir)
            |> Seq.collect (fun s -> getFilesHelper files s)
            |> Seq.append (Directory.EnumerateFiles(dir))

    getFilesHelper Seq.empty directory

let printFiles files =
    Seq.iter (fun f -> ignore(printfn "%s" f)) files

let files = getFiles "C:\Temp"
printFiles files

Pipelining to the rescue!  This is much more readable!  The getFilesHelper function now reads what it does:  Enumerate the directories collect the files in each directory and append the results together2. This is the part of F# that makes me smile!

Step 6: Make it generic functional

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
open System.IO

let actOnFiles directory fileOp =
    let rec getFilesHelper files dir =
        Directory.EnumerateDirectories(dir)
            |> Seq.collect (fun s -> getFilesHelper files s)
            |> Seq.append (Directory.EnumerateFiles(dir))

    getFilesHelper Seq.empty directory |> fileOp

let print files =
    Seq.iter (fun f -> ignore(printfn "%s" f)) files

actOnFiles "C:\Temp" print

Here’s the beauty of functional languages!  We created a higher-order function called actOnFiles (fka: getFiles) and pass print into it.  Now we have a function that takes two arguments: a directory and a function to apply to each file in that directory and its subdirectories.  Here are two more higher-order functions to demonstrate the point

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
open System.IO

let actOnFiles directory fileOp =
    let rec getFilesHelper files dir =
        Directory.EnumerateDirectories(dir)
            |> Seq.collect (fun s -> getFilesHelper files s)
            |> Seq.append (Directory.EnumerateFiles(dir))

    getFilesHelper Seq.empty directory |> fileOp

let print files =
    Seq.iter (fun f -> ignore(printfn "%s" f)) files

let getSize files =
    Seq.map (fun f -> (new FileInfo(f)).Length) files |> Seq.sum

let delete files =
    Seq.iter (fun f -> File.Delete(f)) files

actOnFiles "C:\Temp" print
actOnFiles "C:\Temp" getSize
// actOnFiles "C:\Temp" delete //DO AT YOUR OWN RISK!!!

So that’s it! By focusing on small problems we have achieved function composition to solve a larger problem; and in doing so have actually made our code more extensible. By using the interactive window I could test small portions of the code (enumerating files directories determining how to find the file size etc.) before putting them to use in the larger context of the problem.

Stepping back all we really did was employ a basic problem solving skill: Breaking a problem down into manageable pieces and solving each piece one at a time. Imagine that!

1 Please pretend that there isn't an option on Directory.EnumerateFiles to simply bring back all the files recursively. That would just spoil our fun!
2 I do not believe that this recursive call uses [tail recursion](http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx) because the result of the recursive call is used when being appended to our "collector" variable.