joncfoo

Using F# on Linux

Posted on 2016-01-16

Today I figured I’d dabble with F# and the .NET/Mono eco-system on Linux. I wanted to assess how viable it would be to use F# on Linux for somewhat serious development work.

I figured the following were good goals to tackle:

  1. Get “Hello World” working
  2. Figure out how best to manage library dependencies
  3. Make use of some testing framework
  4. Take a peek at build system(s)
  5. Profit?

Goal #1 - Hello World

I landed on http://fsharp.org/use/linux/ and followed “Option 1” since I am already using a variant of Ubuntu.

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list

sudo apt-get update
sudo apt-get install mono-complete fsharp

After I punched in the above I had fsharpc available - onwards!

I put the following in Hello.fs

let _ =
  printfn "Hello World"

and compiled it with fsharpc -o hello.exe Hello.fs which produced hello.exe

Running it with mono ./hello.exe yielded:

Hello World

Success!

Notes

The output file required by fsharpc needs to end with .exe for an executable

The generated executable must be invoked with mono - similar to java MyClass.class or python my_script.py

From what I gathered it is possible to get the Linux kernel to register the .NET/Mono executable type and execute it correctly if the .NET/Mono executable is invoked by itself - this was not a big deal to me so I didn’t bother looking into it.

Goal #2 - Library dependency management

A quick scouring of the interwebs led me to Nuget and Paket.

Nuget appears to be a package repository whereas Paket manages the relationships between packages. Paket can not only use Nuget as a source of packages but also Git and HTTP. This is similar to Go’s import feature.

Armed with that info let’s grab a copy of Paket.

mkdir -p HelloWorld/.paket
cd HelloWorld/.paket
wget https://github.com/fsprojects/Paket/releases/download/2.44.4/paket.bootstrapper.exe
cd -
mono .paket/paket.bootstrapper.exe

I now had paket.exe in HelloWorld/.paket/ ready to be used.

Notes

Nuget offers an executable of it’s own…I’m not clear why I would want to use it if dependency management isn’t included.

Paket uses a bootstrapper which grabs the actual Paket executable…why not point users to the target executable in the first place?

Telling users to store the Paket bootstrap executable in their source repo…it’s a little unusual compared to other eco-systems. I’m not sure why this is recommended.

Also, why isn’t there a checksum available of the bootstrapper? Maven at least provides this along with a signature.

Goal #3 - Make use of some testing framework

Another quick search led me to http://fsharp.org/guides/mac-linux-cross-platform/#unit-testing

Fuchu appears to be a test organizer and only provides the bare minimum required for assertions. It’s meant to be used with other test frameworks. It reminds me of Mocha - better add it to the list :)

I followed the links in Fuchu’s docs to FsUnit & Unquote.

FsUnit seems to introduce BDD style assertions to other test frameworks similar to should.js. The default package provides extensions to NUnit (JUnit for .NET). Let’s see what else is out there.

Unquote…well…uh…

Yep I’m gonna use that! I understand that there might be a performance penalty but I can only imagine how much time this will save down the road.

So, Fuchu & Unquote it is. Let’s do it!

First, let’s get the dependencies.

The following goes in HelloWorld/paket.dependencies

source https://api.nuget.org/v3/index.json

nuget Fuchu
nuget Unquote

And now grab the dependencies via mono .paket/paket.exe install

Paket downloads our dependencies and puts them in a folder called packages.

Simple code in HelloWorld/src/Hello.fs

module Hello

let hello str = sprintf "Hello %s" str

And the test in HelloWorld/test/HelloTest.fs

module HelloTest

open Fuchu
open Swensen.Unquote

[<Tests>]
let tests =
  testList "Hello" [
    testCase "One word" <|
      fun _ -> test <@ Hello.hello "world" = "Hello world" @>
    testCase "Two words" <|
      fun _ -> test <@ Hello.hello "there joncfoo" = "there joncfoo" @>  // faulty
  ]

[<EntryPoint>]
let main args =
  defaultMainThisAssembly args

Compile & link our test:

mkdir _build
fsharpc -o _build/hello_test.exe \
  -I packages \
  -r:Unquote/lib/net45/Unquote.dll \
  -r:Fuchu/lib/Fuchu.dll \
  src/Hello.fs \
  test/HelloTest.fs

Before we execute the test we need to put the referenced dlls in the same directory as the test executable

cp packages/Fuchu/lib/Fuchu.dll packages/Unquote/lib/net45/Unquote.dll _build/

And now run it:

mono _build/hello_test.exe
Hello/Two words: Failed:


Hello.hello "there joncfoo" = "there joncfoo"
"Hello there joncfoo" = "there joncfoo"
false


 (00:00:00.0983634)
2 tests run: 1 passed, 0 ignored, 1 failed, 0 errored (00:00:00.2632258)

Neat!

Notes

Nuget lists two feed URLs…why? What is the difference? It’s not clear on the homepage.

In Paket’s Getting Started guide, the Nuget v2 feed is referenced without mention of the other one. I gave the v3 URL a shot and it worked.

There are a variety of testing libraries and frameworks but not overwhelmingly so like on the JavaScript side. Kudos.

Running the executable without its dependencies leads to very unclear error messages. e.g. if you remove the dependencies from _build and run mono _build/hello_test.exe you get:

Unhandled Exception:
System.BadImageFormatException: Could not resolve field token 0x04000007
File name: 'hello_test'
[ERROR] FATAL UNHANDLED EXCEPTION: System.BadImageFormatException: Could not resolve field token 0x04000007
File name: 'hello_test'

The error could at least say something like “referenced assembly X.dll not found”

If you’re new to F# and .NET/Mono (like myself), you should definitely read http://fsharp.org/guides/mac-linux-cross-platform/ It has some good nuggets of info in there. Skip the parts you don’t need for now (trust me - it’ll save you a lot of unnecessary headache).

Goal #4 - Take a peek at build system(s)

On Cross-Platform Development with F# the first build system mentioned is xbuild/msbuild. I’ve heard enough people complain about it so I’m gonna stay away.

Makefiles are next - I can do that I suppose but I’d rather not.

Fake looks like Ant but in F#…reminds me of Gradle about 6-7 years ago. My initial thoughts are “I don’t need this right now, perhaps later.”

For now I’ll stick with fsharpc in a shell script:

#!/bin/sh

fsharpc -I packages \
  -r:Unquote/lib/net45/Unquote.dll \
  -r:Fuchu/lib/Fuchu.dll \
  src/Hello.fs \
  test/HelloTest.fs \
  -o _build/hello_test.exe

Goal #6 - Profit?

I’ve noticed a big leaning towards IDEs in the .NET community as a whole & the focus on tooling is certainly reflective of that. Coming from an alternative background this seems rather peculiar to me but I think I get it. Though it does make me wonder if automation [via scripting] is troublesome in .NET

Concerning F#…in the limited amount of time I spent exploring the eco-system, I must say that I was pleasantly surprised!

I’m looking forward to working with F# on *nix!