Last week I had the crazy idea to build a basic web server with F# on my Linux system.

I’m spoiled by Vim’s language support for other languages: hover information, autocomplete, etc.
The experience is nearly as good as using VS Code. But (Neo)Vim doesn’t come with all the cruft of Microsoft’s Electron-based editor.

I thought it would be trivial to get decent language support F#. After all, I’ve already done the work of setting up the necessary plugins and configuration for Vim.

The bulk of the language support - be it for VS Code or for Vim - comes from the Language Server Protocol (LSP):

The Language Server protocol is used between a tool (the client) and a language smartness provider (the server) to integrate features like auto complete, go to definition, find all references and alike into the tool

All you need is a language server and a language client. Ideally, it comes with linting and formatting support. If not, the community will have tools available. Those work well with different Vim plugins.

F# is an open-source language, but it’s a Microsoft brainchild. As a Linux user, using F# is a painful experience compared to other languages, where Linux is a first-class citizen.

For example, setting up support for Go took me 5 minutes.

After I spend several hours doing the same for F# I want to share my experiences in the hopes of sparing others the trouble.

F# Installation

Installing F# is trivial and without hassle. You need the .NET Core framework, an open-source framework for C#, Visual Basic, and F#.

For Arch Linux, it’s as easy as downloading the dotnet-sdk and the dotnet-runtime with the Arch package management tool:

yay -S dotnet-sdk dotnet-runtime

(yay is awesome, you should try it as an alternative to Arch’s pacman.)

If you don’t use Arch, you can find installers for different platforms.

Language Server Protocol

F# offers multiple language servers and Vim plugins aimed at giving you a better development experience:

All are wonky or make trade-offs that I don’t like. Some don’t work at all.

Here are my experiences:

1. Ionide-vim

Ionide-vim is tightly coupled to autozimu/LanguageClient-neovim, a language client for Vim and NeoVim.

If that’s the language server client you’re using, then go for Ionide-vim.

I switched from LanguageClient-neovim to the lightweight vim-lsc by Nate Bosch. vim-lsc uses pure VimScript. LanguageClient-neovim was originally written in Python. It needs extra dependencies and was a bit slow. (It now uses Rust, so is probably faster now. But still: extra deps).
For now, I’m a happy user of vim-lsc. I don’t want to go back to LanguageClient-neovim.

(For a more in-depth guide, read LSP in Vim with the LSC Plugin.)

2. FsAutoComplete

The FsAutoComplete project (FSAC) provides a backend service for rich editing or intellisense features for editors.

This project, written in F#, aims to provide a complete suite: linting, formatting, hover information, go to definition, etc.

Unfortunately, it’s totally broken on Linux.

Additionally, the installation instructions suck if you’re an F# newbie.

If you want to give it a whirl, you need to grab a packaged release. As we’re using .NET Core, download the fsautocomplete.netcore.zip from the releases tab and unzip it somewhere on your computer.

These are the settings I tried with vim-lsc:

let g:lsc_server_commands = {
\  'fsharp': {
\    'command': 'dotnet ~/.bin/FsAutoComplete/fsautocomplete.dll --background-service-enabled',
\    'log_level': -1,
\    'suppress_stderr': v:true,
\    'workspace_config': {
\        'initializationOptions': {
\            'AutomaticWorkspaceInit': v:true,
\       },
\       'Fsharp.dotNetRoot': '/usr/share/dotnet',
\       'Fsharp.useSdkScripts': v:true
\    },
\}

Point the command to the location where you extracted the FsAutoComplete binary. The intializationOptions are my fruitless attempt to mitigate the errors I encountered:

Cached typecheck results not yet available

See issue #583. The issue is closed, but I couldn’t get FsAutoComplete working with Vim on Arch Linux.

3. vim-fsharp

vim-fsharp is a wrapper around FsAutoComplete. It uses Mono, the predecessor of the .NET Core framework which comes with its own set of problems.

For example, I got errors like fsharpi missing, and I have no clue how to install it. I’m not the only one.

So that was a dud, too.

4. fsharp-language-server

fsharp-language-server is a straightforward implementation of the language server protocol - nothing more, nothing less.

It often sputters, as you can see looking at the open issues on GitHub.

I still get warnings when I run the language server, but it works most of the time.

If you use Arch Linux, you can install it via the AUR:

yay -S fsharp-language-server

If not, download a packaged release and extract the files.

Then run the following command from your terminal (inside the folder of the downloaded files): dotnet build -c Release.

Search for the FSharpLanguageServer.dll file and point your language client configuration to the file:

let g:lsc_server_commands = {
\  'fsharp': {
\    'command': 'dotnet ~/.bin/fsharp-language-server/src/FSharpLanguageSerer/bin/Releasen/netcoreapp3.0/FSharpLanguageServer.dll',
\    'log_level': -1,
\    'suppress_stderr': v:true,
\  },
\}

If you open a F# file (.fs), you’ll still get errors like FSharp.Core.dll not found (see issue #67), but the language server works.

Linting and Formatting

I use ALE, the “Asynchronous Lint Engine” to check the syntax of my files and to format them.

Disappointingly, there is no support for F#.

You can install the tools FsharpLint and Fantomas and run them from the command line.

dotnet tool install -g dotnet-fsharplint
dotnet tool install -g fantomas-tool

Inside Vim, you can run an external command from the command mode if you like.

When you’re inside Vim in normal mode, type this:

:execute '!fantomas %' | edit

Then hit the Enter key.

Or

:execute '!dotnet fsharplint lint %' | edit

and Enter.

The commands will open a sub-shell that will execute the commands and then reload the current file in Vim.

I sometimes abuse Makefiles as task runners to automate running the commands. You could use something similar, for example GitHub hooks.

Conclusion

F# support is confusing to set up for Linux. It doesn’t help that there are two frameworks for cross-platform development, Mono and .NET Core.

Generally, it feels like Linux support is not as mature as I’m accustomed to.
That makes sense, since F# is a Microsoft language.
But it’s the first time that I feel like Windows users will have a leg up on programmers that use Unix.