A brief F# exploration

I have been writing full-stack web apps in Haskell using functional reactive programming (Reflex-FRP) for 3 years now. Curiosity stuck me as to find out what the FP languages other than Haskell had to offer in this area.

My critieria were:

  • Must be a functional programming language
  • Should compile to JS or Wasm (cf. No JavaScript)
  • Must run natively on backend without nodeJS (rules out the likes of PureScript)

Haskell’s GHCJS (esp. when used with Obelisk) satisfies all of this, but there is one pain-point: the future of GHCJS (which has not been updated in a year) and Reflex seems to be in the hands of one small company, Obsidian Systems.

That lead me to F#, a hybrid FP language (“hybrid” because it supports OOP, which is essential to integrate with the rest of the .NET ecosystem). I’ve documented my learnings here.

What I found impressive:

  • Full access to the entire .NET ecosystem of libraries and frameworks (which is larger than that of Haskell).
  • .NET 5.0 ecosystem is a pleasure to work with (and it works well on Linux with VSCode); and you can create cross-platform apps more straightforwardly than in Haskell, including on mobile devices.
    • If I were to start developing Neuron today, I would certainly consider F# (but see below).
  • Microsoft has a great full-stack web development story; and they support WebAssembly (Blazor), including a framework for real-time communication (SignalR).
    • In F#, Bolero today is the go-to framework to make use of the above technology.
  • I find it reassuring that I can rely on Microsoft to advance the full-stack web development more than one small consultancy (Obsidian Systems) with less than transparent open source development in the Haskell land.
    • That said, I have some hopes that Tweag’s Asterius catches up, and the community is encouraged to proliferate a whole new ecosystem of full-stack development tools in Haskell not necessarily tied to Reflex.

Some things are better in the Haskell ecosystem, though.

  • Fast development reload workflow works super well in Haskell, thanks to ghcid. In .NET, you have dotnet watch - but that recompiles the whole project on every change leading to annoying delay1; it made me switch back to using Haskell for DSL-based static sites, while live-reload is essential to get quick feedback on things like CSS changes.
  • Having to work with OOP-based .NET libraries (written in C#) can be an annoyance from a pure-FP perspective, though that can be dealt with by wrapping these libraries in a functional layer, and then using that in the F# program.
  • Overriding dependencies to use a fork in straightforward manner is virtually impossible. You have to create a local Nuget repo containing the binary of your overriden dependency. Whereas in Haskell world, one can easily use Nix to use a Git repo (Neuron does this) as a package dependency.2

F# will continue to remain in my toolbox. If the aforementioned downsides are addressed in some way, I might just pick it for some of the next projects over Haskell, which is still my go-to language today.

See also

  1. .NET 6.0 will address this at least for Blazor projects.

    ↩︎
  2. No, Paket’s Git feature does not support this.

    ↩︎