F# Has Won Me Over: Coming to .Net World from Outside .Net

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.

Over the Christmas holidays I dived into F# by taking courses on FSharp TV and by going through content from Scott’s wonderful blog Fsharp For Fun And Profit.

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?

The tool for this is Paket which uses the Nuget package manager under the hood. Again, Visual Studio Code has a plugin to integrate with Paket, Ionide-Paket

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

mono name-of-your-executable.exe

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.

Select Build

You will be prompted for a FAKE build task to run.

Select build.

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.

Install that.
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

FSharp.Core
FsUnit

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 16.1.0.0
  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 [0]
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
open Fake

add

open Fake.Testing

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 [0]
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

Conclusion

Hopefully this has helped people to get started with the F# and .Net ecosystem and get up and running quickly in this amazing language.

12 thoughts on “F# Has Won Me Over: Coming to .Net World from Outside .Net

  1. Nice stuff! Couple of things: –

    1. You might want to check out the Expecto test library. It’s very lightweight and runs within any executable – plus (AFAIK) there’s now native support for it within Ionide.

    2. Paket doesn’t actually use the Nuget Package Manager under the hood – it’s actually a complete replacement for it. It happens to allow you to use NuGet packages – so it’s a drop-in replacement for Nuget – but it’s actually a completely separate product 🙂

  2. One thing that tripped me up in Visual Studio Code was that after pressing Shift-Cmd-P I typed “F” and immediately got 0 results. It was important to press “>” first

  3. Here is what I get when I try to run the tests:

    1) Error : MusicLibraryTest.Example Test
    System.IO.FileLoadException : Could not load file or assembly ‘FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
    at MusicLibraryTest.Example Test()

    I am new to .Net (am an FSharp-curious linux guy), my best guess is that the NUnit machinery is expecting to use FSharp.Core 4.3.1, conflicting with the rest of the project, which is using FShare.Core 4.0.0 per the packet.lock file and the fsproj files.

    Not really sure how to handle ; any advice would be appreciated.

    1. Yeah I’ve heard of .NET Core too but, I’m not sure if it’s as mature as Mono but I’m new to .NET world so I don’t know for sure.

Leave a Reply

Your email address will not be published. Required fields are marked *