Last Tuesday I started a book club with my friends Samir Talwar and Pedro Moreira Santos (both of them working at Codurance). The book we've choseen is "The book of F#", because we want to learn F#.
After reading the first three chapters we decided to make the FizzBuzz kata to start practicing. In this article I'm going to explain you how we solved the kata trying to explain all the F# syntax we use. So, let's start!
First of all let's install NUnit as our testing framework. In future articles we are going to take a look to other testing frameworks and libraries, but right now NUnit is a good choice.
No F# magic, just NuGet.
Let's write our first F# line of code:
In this line we have declared a new module. In F#, unless you have only one single file in your project, every file must begin with a namespace or module definition. Both are ways we have to organize our code. The main difference is that inside a module you can define other modules, type definitions, values and functions but inside a namespace only modules and type definitions.
Now we need to include NUnit in the project. To reference a package in F# (a using in C#) we use the open keyword:
As we are working in a module, we can directly write the first test:
In the line 1 of this gist we can see how we use an attribute in F#, in this case the Test attribute from NUnit.
In the line 2 we are defining a new function, in this case a function without any parameter. To do that, we use the let binding to identify the function. We are not going to see any example in this article, but it's worth mentioning that let bindings are immutable.
The next interesting thing is how we name the function. In F# we can use any type of string to identify a value or a function, we just need to change what we use to wrap it. In this case we are using a quoted identifier to use a string with blank spaces, just enclosing it in backtick characters (``).
Instead of using brackets to define the body of a function in F# we use an equal after the parameter definition and we indent the body. It's no important how many whitespaces we use as long as we are consistent in the indentation level we use.
And finally, in the line 3, we are making the assertion of our test. In this case we are asserting that "1" is equals than the result of calling a function called fizzBuzz with the number 1 as a parameter. In F# we don't use parenthesis to pass parameters to a function.
Not bad for three lines of code. But this code don't compile. We need to declare the fizzBuzz function in order to be able to run the tests. Let's do it:
Here we are defining a function with one argument that returns an empty string. In F#, unless we are defining a function with more that one argument and with type annotations in the arguments (we'll see type annotations later), we don't need to use parenthesis to declare a function.
In F# all expressions (and therefore all functions) returns a value, so the last expression of a function is the return value. So, in this case, the expression "" is the return value of our function.
If we now run the test, the test will fail. To make this test pass, you just only have to return "1"
As you can imagine, the second test will be just as the first test but changing 1 with 2. Let's see how we have to implement the function to pass it:
Here you can see our first flow control expression, in this case an if. In F# ifs are defined with the expression if...then...else. To compare to values we use the single equal operator.
In the third test we are going to test that the fizzBuzz function returns "Fizz" when the parameter is a 3. Let's see the changes we must do in the function:
In this case we've introduced an elif (a shortcut for else if) to specify the second condition.
After the next test ("4" = fizzBuzz 4) we have enough duplication to do a little refactor. The result of the refactor can be something like this:
As you can see we can use the standard .Net ToString function here.
The next important bit is after writing tests for 6 and 9. The code of the fizzBuzz function could be something like this:
If you follow the rule of three, it's time to refactor. Our firs refactor could be something like this:
We are using the exists function from the List module. This function takes a predicate (a function) as a first argument and the list of elements as the second one. We are checking that the input is one of the elements of the list.
We can rewrite this piece of code in a more compact way taking advantage of currying. You can read more information about currying in this post.
With the use of the parenthesis, we are converting the = operator into a function. So, when the List.exists supplies the current list item to our predicate, we can apply the function to compare it with the input parameter.
If we continue working we will end up with something like this:
Now it's time to apply a typical functional feature: pattern matching. Pattern matching is F# primary branching mechanism. So, we can remove these nested ifs with a pattern matching. Pattern matching is a quite big concept (we'll explore it deeply in the future) but here we are going to apply some concepts. Let's start with the simplest refactor we can do:
Here we are matching a tuple. The underscore character (_) is the Wildcard pattern that match any value and doesn't bind it to anything.
Finally we are going to apply a couple of refactorings more to reach our final implementation:
The first refactor we made is take advantage of the NUnit's test cases to simplify the test code and remove duplication.
The second refactor is to organize the code in different modules (nested modules), one for the production code and other for the test code. In F#, the order of the modules is important because F# code is evaluated from top to bottom. So, if we need to reference module Prod from module Tests, we need to declare it before.
And that's all. I hope you liked this little F# introduction. I'll keep the blog updated with the new things we'll be learning.