My First F# App™

I’ve been eyeing up F# for some time after reading that it’s the hot new thing. Unfortunately when it comes to learning new ideas or technologies, I find it difficult to actually learn them unless I’m applying them at the same time – so I was in a vicious cycle of needing an application to write, in order to learn to write an application!

This is where Project Euler comes in handy. I’m not mathematically gifted, but if you want to learn how to program in a more functional way then I’d definitely recommend tackling some of their problems and striving to avoid falling back on imperative habits at all times. Solving some of these problems actually gave me a breakthrough moment in understanding recursion, which I knew about from university but never truly grokked until now.

I was also aided by reading Real World Functional Programming, authored by Tomas Petricek and Jon Skeet. Not only does it show you stuff in F#, but it shows you things that you can be applying in C# today at your current company that wouldn’t let you write F# in a million years! 

Whilst thinking about how large the codebase in my current day-job project was becoming I finally had an idea for a toy application that wouldn’t be too hard to implement: a console app that counted the lines of files in a directory. I’m sure it’s been done elsewhere by other people, but if you ever think of something simple like this then seize it as a learning experience.

The code for my LineCounter project sits here. It shows a few features of F# that I think are pretty cool. Firstly and secondly is returning multiple values and array slicing:

let directory, searchPatterns =
match argv with
| [| |] -> failwith "Directory must be provided as first argument"
| [| directory |] -> directory, [| IncludeKey; "*" |]
| _ -> argv.[0], argv.[ 1.. ]
view raw array splice hosted with ❤ by GitHub

This bit of code is using pattern matching on the array of command line arguments to get the file directory and file search patterns.

_ -> argv.[0], argv.[ 1.. ]

This last pattern matches the wildcard to get the file directory (element at index 0) and then the file search patterns (elements from 1 to last).

Next is the fold function with record types:

let splitIncludesAndExcludes =
searchPatterns |>
Seq.fold(fun (acc,collecting) next ->
match next with
| IncludeKey -> (acc, Include)
| ExcludeKey -> (acc, Exclude)
| next ->
match collecting with
| Skip -> (acc, collecting)
| Include ->
({ acc with Includes = next::acc.Includes }, Include)
| Exclude ->
({ acc with Excludes = next::acc.Excludes }, Exclude))
({ Includes = []; Excludes = []; }, Skip)
|> fst

This iterates over the arguments, splitting them into two lists: the file patterns to include, and the file patterns to exclude. Skip, Exclude and Include essentially take the role of an enum in this instance. The accumulated value is a tuple of a type that has a list of excludes and a list of includes as strings, and the “enum” to determine what to do in the next iteration. You can see in

{ acc with Includes = next::acc.Includes }

the handy “with” syntax that copies an immutable data structure with selected properties changed. This chunk of code actually took me a while to nail down, but once it was compiling, that was it. I’ve encountered this before in F#, where the language so clearly expresses what I want to do: the process is think, write, then…hey presto it compiles and does exactly what you expected!

Lastly is backticks:

[<NUnit.Framework.TheoryAttribute>]
member x.``should check all files when not given specific patterns``() =
let actual =
LineCounter.countLines
(System.Environment.CurrentDirectory + @"\Samples\Nested")
(Seq.singleton "*")
Seq.empty
Assert.AreEqual(3, actual)
view raw backtick hosted with ❤ by GitHub

This allows you to write expressive unit test names in plain english, which is very similar to what I’d do with QUnit for javascript testing (albeit the test names in that are just strings).

I’d love to write F# for a living, but whilst people are still struggling with object oriented programming it will probably be another decade before functional programming is mainstream…

Leave a comment