Feb 27, 2017

Can F# be liberated from the .NET architecture?

Recently I've been thinking about the delicate situation that the F# language designers find themselves in whenever they want to introduce new features (especially semantics) into the language, or whenever the C# team want to introduce features that may affect that language or the common language runtime.

The problem has clearly existed for a while; F# hasn't gotten any significant new features since its last major release. The latest example to pop up is probably the uncertainty over adding typeclasses, especially now that it looks like C# is thinking about adding them too.

So how can F# be freed from its ties to C# and the .NET runtime? Well, one way would be to split the F# compiler into two parts: the frontend that compiles F# code to its own intermediate language (let's call it F# Intermediate Language, or FSIL); and the backend that compiles FSIL to some other target language like MSIL (or JavaScript like Fable, JVM bytecode, native, ...).

Compiling F# to FSIL

This presents the main advantage that the F# language can evolve independently of C# and the CLR. The compiler frontend need only worry about doing a great job of implementing F# syntax and semantics in terms of a single target representation that it controls fully. The language can rapidly experiment with new syntax and semantics, or in fact with the traditional ML language family semantics, just by defining a general FSIL to target.

Some examples of this would be implementing typeclasses, or ML-style parameterised modules (functors), or Scala-style implicit evidences, or an easy-to-use call-by-name function parameter declaration syntax for easier control-flow DSLs, or higher-kinded types, or ... any of a myriad of things. All you have to do is teach the frontend to understand your new syntax and compile it to FSIL.

The nice thing about FSIL is that it would preserve nice concepts like units of measure, typeclasses, ML modules and functors, etc., because it was specifically built to understand them. F# modules would be compiled down to FSIL files on a one-to-one basis and F#-specific libraries could be distributed as FSIL packages. Users could link against them to get the nice higher-level functionality that MSIL doesn't offer.

Compiling FSIL to MSIL (and others)

This step would be concerned with the best way to encode FSIL in terms of the target language or runtime. E.g., currently F# modules are encoded as static MSIL classes. This is obviously a great fit, and others may be found for higher-level F# features.

But no doubt some FSIL functionality would be erased--e.g. functors would be defunctorised and compiled away to nothing. Any concrete instantiations of functors would survive as normal modules encoded as static classes as usual. Or, typeclasses would be compiled down to pass instances in explicitly in the MSIL encoding.

With this organisation, other backends could spring up. Fable could become a backend from FSIL to ES5/6/.... Someone could write a backend for FSIL to JVM, or the Erlang runtime (BEAM), or the OCaml runtime. There are quite a few possibilities opened up by decoupling the two ends from each other.

Optimisation

With FSIL we would have the opportunity to do separate optimisation passes on the higher-level, typed, F# focused FSIL, and on the lower-level CLR (or other target) outputs. We would be able to take advantage of the information that we would have at those points in the process while not worrying about the previous or following stages.

F#ixing up

There are many advantages to a decoupled frontend/backend design for the F# toolchain. Possibly the biggest one is that it places F#'s future as a powerful language squarely in the hands of the F# language designers, and lifts from them the burden of having to worry about compatibility with the rest of .NET.