F# is a language that was sitting in the corner calling to me for a long time tempting me to go take a look at it. I watched Scott Wlaschin’s fantastic talk on Functional Programming Design Patterns some time ago. This talk is a walk through of common FP design patterns with the example code being in F#. Scott put together this talk so well that it is not a blocker to getting the most from the talk if you don’t have any previous knowledge of F#. At the same time, coming from mostly programming in Scala professionally, I couldn’t help but admire the beauty of the F# code in his examples. So I had a quick look further at some F# code out there. Things like a simple syntax using whitespace, automatic currying, nice concise representations of Algebraic Data Types caught my eye immediately.
F# has quickly become my favourite language to code in along with Haskell. Along the way though, there were some stumbling blocks getting used to the .Net world. Most of these could have been avoided if I just used the Xamarin Studio IDE. However, I’m glad I didn’t and took a little pain along the way as it made be explore the .Net and F# ecosystem. I did start to use Visual Studio Code which is really a text editor with a load of great plugins out there for different languages. Its lightweight enough that you still have to find out what’s happening under the hood a bit with your F# .Net projects but, at the same, it does make things like creating projects with common templates, building and adding references to existing projects a lot less painful. Incidentally, there is now a Haskell plugin for Visual Studio Code which uses Intero. This is very exciting indeed and I can’t wait to try it out!
How do I code in F# on a mac?!
Yep this is a question I had for a while too. That is until I discovered Mono, an open source .Net framework which is cross platform.
I found the best way to get F# set up on OSX was to install Mono but to actually use the installer that can be downloaded here. I tried installing the homebrew way first but this seems to be missing some .Net libraries. For example, one that is needed for System.Drawing. I uninstalled my homebrew mono installation and installed from the Mono download that I linked to above, and I was then able to build and draw lovely spirographs from the FSharp TV, Introduction to F# course.
How do I create an F# project?
This was a question I had too. I wanted to simply create a project with a common folder structure. I was looking for something similar to lein new in Clojure or stack new in Haskell. A great tool for this is Forge. The Visual Studio Code ionide plugin integrates with Forge to make it really easy to create new projects through the command palette in Visual Studio Code. I demo this further down.
How do I manage dependencies?
How do I build an F# project
FAKE is a very popular build tool for F# and allows you to specify your build tasks through a build.fsx file using and F# DSL. Visual Studio Code has a plugin to integrate with this too as part of its ionide suite, Ionide FAKE This is demoed further down in this blog too.
How do I run a .exe on Mac!
Some of these tools I have mentioned along with the nunit testing tool (which I demo further down) require the running of a .exe to perform tasks. If the .exe is one that doesn’t run directly on Mac OS or Linux, usually it can be run using
once mono is installed and on your path and once name-or-your-executable.exe is actually a file that can be executed. It may seem like a simple thing but it is important to know as there are resources out there that describe how to use .Net tools assuming you are on a Windows machine.
Demo Time: TDD with F#
As there are resources out there about setting up F# with Mono and Visual Studio Code, there’s no need to re-iterate them here so I will assume that you managed to get those set up. If you have questions, feel free to comment on this blog.
My main stumbling block coming from outside the .Net world was simply setting up a unit test project and linking this to a production code project (by production code, I mean implementation code).
Lets go through that here:
So in Visual Studio Code, from an empty directory, open command palette with shift-command-p and type F#.
Then select New Project.
You will be prompted to select the type of project.
Choose classlib for our production code.
You will then be prompted to choose the project directory.
Lets specify a directory called MusicLibrary. So type in MusicLibrary, hit enter and you will be prompted to provide a project name. Lets call this MusicLibrary too.
After a second or so, you should eventually see that the ionide plugin has created three directories, .packet, MusicLibrary and packages. It will have also created a build.fsx file and a build.sh file so that we can build our new MusicLibrary project with FAKE.
In the MusicLibrary directory, you will find a newly created MusicLibrary.fs file.
This is where we are going to put our production code after we write a test for it.
For now, delete everything from there and we are going to add a function to test called addSongToLibrary which will eventually take a list of existing song titles and a new song title and return a list of song titles containing our the existing ones and our new title. For now, it just returns an empty list until we have our test ready. The contents of the MusicLibrary.fs file can be replaced with:
module MusicLibrary let addSongToLibrary existingSongs newSong = 
Now, we should be able to build this right?. Lets give it ago.
Open the Visual Studio command palette again and type FAKE.
You will be prompted for a FAKE build task to run.
Now we get an error:
If you look down at the output window in Visual Studio Code, you will see something like
build.sh: line 34: syntax error: unexpected end of file
We need to convert the dos .sh file to into unix format. There is a tool for this on the Mac App Store dos2unix.
In the terminal go into the parent directory of your MusicLibrary directory where build.sh exists and run dos2unix on the build.sh file.
dos2unix build.sh dos2unix: converting file build.sh to Unix format...
Now follow the steps earlier to try and do a FAKE build in visual studio code.
In the output in visual studio code, you should see output about using paket to get any needed dependencies and also about the creation of dlls.
From my understanding Dlls in .Net are like jar files in the jvm world and they are the binaries created as a result of compiling F# code. The FAKE build will now have created a build directory and put the dlls in there.
You will see a MusicLibrary.dll in there.
Back in the MusicLibrary directory there is also a MusicLibrary.fsproj file which FAKE uses to find out things it needs for the build. When we write our test, we will see how we can add a reference to the MusicLibrary.dll into our .fsproj file for our unit test project. If you look in build.fsx, you will see a reference to these .fsproj files for FAKE.
let appReferences = !! "/**/*.csproj" ++ "/**/*.fsproj"
Lets set up our test project so we can test our addSongToLibrary function!
To test our addSongToLibrary function we can set up a MusicLibraryTest project. Follow the same steps as we did for creating the MusicLibrary project in visual studio code except for the prompt to choose library type, choose fsunit
After, you should see a newly created MusicLibraryTest directory.
If you go into that directory, you will see that there has been a MusicLibraryTest.fs file created for us containing:
module MusicLibraryTest open NUnit.Framework open FsUnit [<Test>] let ``Example Test`` () = 1 |> should equal 1
There are 2 libraries imported here for unit testing, NUnit.Framework and FsUnit.
This is where paket comes in for our test dependencies. If you go into the paket.references file within the MusicLibraryTest directory you will see
So, we need to link our MusicLibraryTest project to our MusicLibrary.fsproj so that we can test our addSongToLibrary function. This is taken care of by add a reference to the MusicLibrary.fsproj in the MusicLibraryTest.fsproj.
Open up the MusicLibraryTest.fsproj file.
Now, open visual studio command palette again.
Select Add Project Reference.
Then choose MusicLibraryTest as the project that you want to edit
Then choose MusicLibrary as the reference
Now, somewhere in the MusicLibraryTest.fsproj, there will be a newly created reference to the MusicLibray.fsproj. Search the MusicLibraryTest.fsproj for MusicLibrary.fsproj.
Now we can reference our MusicLibrary module and call the addSongToLibrary function.
Back in the MusicLibraryTest.fs file, we can import the MusicLibrary module which contains our addSongToLibrary function and lets write a test to test adding a song title.
Replace the contents of MusicLibraryTest.fs with:
module MusicLibraryTest open NUnit.Framework open FsUnit open MusicLibrary [<Test>] let shouldAddASong () = ["Sir Duke";"Superstition"] |> should equal (addSongToLibrary ["Superstition"] "Sir Duke")
How do I run my test!
First of all lets create our MusicLibraryTest.dll. This is done the same way that we created MusicLibrary.dll before by running the FAKE build in visual studio code. After running the build, there should be a MusicLibraryTest.dll in your build directory.
Actually getting tests to run was a stumbling block for me. To run tests, a FAKE task can be created in build.fsx. I will show how to do that in a little bit. First I will show how to run more manually using an nunit test runner.
The NUnit3 test runner can be downloaded as a zip file here.
To make this work with a FAKE task a bit later, first create a directory tools/Nunit in the root folder in visual studio code that you have your MusicLibrary and MusicLibraryTest projects in.
Then download NUnit.Console-3.5.0.zip into there.
Then unzip it.
Now we can use the nunit3-console.exe to run our test.
In the terminal, go into the tools/NUnit folder.
Make the nunit3-console.exe executable:
chmod 755 nunit3-console.exe
We can run our test with the command:
mono nunit3-console.exe ../../build/MusicLibraryTest.dll
This should output our failing test result:
NUnit Console Runner 3.5.0 Copyright (C) 2016 Charlie Poole Runtime Environment OS Version: MacOSX 188.8.131.52 CLR Version: 4.0.30319.42000 Test Files ../../build/MusicLibraryTest.dll Errors and Failures 1) Failed : MusicLibraryTest.shouldAddASong Expected is <Microsoft.FSharp.Collections.FSharpList`1[System.Object]>, actual is <Microsoft.FSharp.Collections.FSharpList`1[System.String]> Values differ at index  at FsUnit.TopLevelOperators.should[a,a] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f, a x, System.Object y) [0x0004e] in :0 at MusicLibraryTest.shouldAddASong () [0x0003d] in :0 Run Settings DisposeRunners: True WorkDirectory: /Users/tom/Dropbox/software-development/fsharp/NUnitDemo/tools/Nunit ImageRuntimeVersion: 4.0.30319 ImageRequiresX86: False ImageRequiresDefaultAppDomainAssemblyResolver: False NumberOfTestWorkers: 8 Test Run Summary Overall result: Failed Test Count: 1, Passed: 0, Failed: 1, Inconclusive: 0, Skipped: 0 Failed Tests - Failures: 1, Errors: 0, Invalid: 0 Start time: 2017-01-08 13:40:33Z End time: 2017-01-08 13:40:33Z Duration: 0.142 seconds
Now lets integrate this into FAKE.
In the build.fsx file, add the following near the top underneath
so you now have
open Fake open Fake.Testing
Add our NUnitTest task underneath the Target “Deploy” block.
let testDlls = !! (buildDir + "*Test.dll") Target "NUnitTest" (fun _ -> testDlls |> NUnit3 (fun p -> p) )
So we have:
Target "Deploy" (fun _ -> !! (buildDir + "/**/*.*") -- "*.zip" |> Zip buildDir (deployDir + "ApplicationName." + version + ".zip") ) let testDlls = !! (buildDir + "*Test.dll") Target "NUnitTest" (fun _ -> testDlls |> NUnit3 (fun p -> p) )
Also add NUnitTest to the build order so the end of your build.fsx file should be
// Build order "Clean" ==> "Build" ==> "NUnitTest" ==> "Deploy" // start build RunTargetOrDefault "Build"
Open Visual Studio Code command palette and type FAKE as before
Choose FAKE: Build
Now you should see our new NUnitTest task so select that.
This will fail because of our failing test.
The output pane in visual studio code will have the results of the nunit3-console.exe runner
1) Failed : MusicLibraryTest.shouldAddASong Expected is <Microsoft.FSharp.Collections.FSharpList`1[System.Object]>, actual is <Microsoft.FSharp.Collections.FSharpList`1[System.String]> Values differ at index  at FsUnit.TopLevelOperators.should[a,a] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f, a x, System.Object y) [0x0004e] in :0 at MusicLibraryTest.shouldAddASong () [0x0003d] in :0
Lets make the test pass!!
Go back to the MusicLibrary.fs file and make the test pass by replacing its contents with:
module MusicLibrary let addSongToLibrary existingSongs newSong = newSong :: existingSongs
In Visual Studio Code, use the command palette to run FAKE: Build selecting build as before and then FAKE: Build selecting NUnitTest.
Looking through the output in visual studio code we can now see our passing test report:
Test Run Summary Overall result: Passed Test Count: 1, Passed: 1, Failed: 0, Inconclusive: 0, Skipped: 0 Start time: 2017-01-08 13:46:29Z End time: 2017-01-08 13:46:29Z Duration: 0.132 seconds
Hopefully this has helped people to get started with the F# and .Net ecosystem and get up and running quickly in this amazing language.