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
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
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
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.
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
getFilesHelperfor each subdirectory. The result of
getFilesHelperis a sequence of file names so each folder has the files appended into one large sequence.
- The use of a “helper” function. The
getFilesHelperfunction takes an argument that collects the results of each of the recursive calls. By creating this function as a helper inside the main
getFilesfunction we can spare the calling code those details; and avoid the smell of an output parameter.
Step 5: Clean up
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
Here’s the beauty of functional languages! We created a higher-order function called
getFiles) and pass
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!
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.