Recently, I've been working a bit on my F# language provider for Asp .NET 5 (formerly dubbed
vNext). And today I'd like to talk a bit about where it all stands. What the problems are, and how I hope some of them can be fixed. But before I get started, I'd just like to say that this will probably turn out quite ranty, so consider yourself warned.
The good part
But let's start with the good part of the story, before we get down to mucky terrain. Because there are certainly some good parts to F# in Asp .NET 5 as it stands today. And the picture would not be completed, without also painting these. So let's start at the beginning:
So before we start getting into problems with compilers, and
MemoryStreams being disposed of prematurely, consider the fact that it all actually works. Or well, at least most of it. You can without much problem at all create a Asp .NET 5 application in F#, and it takes less than a minute to set up. Aside from the KRE (if you're not using VS) you don't even have to install anything. Just plug the right settings into your
project.json file and you're good to go. If you'd like to learn more about this, see the github repo here, it should have all the information required to get you started.
It properly interfaces with Asp .NET 5
One of the features that is special in Asp .NET 5 is something called "Assembly Neutral Interfaces" (see David Fowlers blog post on the subject). These are more or less required to use some of the more advanced stuff that's added to Asp .NET 5, as many key interfaces (that can be injected into your classes by means of DI) are assembly neutral.
The F# provider for Asp .NET already works well with assembly neutral interfaces. As a matter of fact, several of them are already in use in the
YoloDev.FSharp.AspNet project itself, and it's not possible to create an Asp .NET language support in a language where you do not support the assembly neutral interfaces.
The bad part
Now that we've looked at the good parts (which are, as said, pretty good. So go test out F# in Asp .NET 5 today), lets look a bit more at the not so good parts.
No ANI creation support
ANIs (Assembly Neutral Interfaces) is a new feature in ASP.NET 5. Currently, the F# compiler for ASP.NET does not support creating ANIs. It supports consuming them though. For the most part, this doesn't really matter. However, it means that if you do want to create ANIs, you'll have to create a thin C# assembly containing them, or manually hack it in one way or another.
This one is almost a given. The stuff is new, and some of it is made by me and not MS, so obviously there is no tooling yet. I have a high hope though, that adding tooling will be quite easy. My guess is that a small modification to F# tooling in Visual Studio is all that's needed. Some hook to identify vNext projects, and hook into the DTH to get the dependencies, and you'd mostly be done.
Before I get on to the last part, I'd like to point out a few things first. First of all, I like F#. I really do. I would not be creating F# support for vNext applications if I didn't. Which leads me to my second part: I am not a good F# programmer. Or so I say at least. I've only been using F# for a short while, and quite a bit of the functional programming ways are still new to me. I have much more experience working with C# or other more C/C++ like languages. Yet I still feel like what I'm about to say needs to be put out there, in hope that things can get better over time.
No PDB support when compiling in memory
The last problem, which I'll add here is something that will hopefully be fixed within a fairly short amount of time (either by me or someone else). Currently you can't write PDBs when you're compiling in memory, because of how the COM api is used.
The compiler is a mess
As stated, I'm not an F# guru. However, the people who wrote the F# compiler probably were. To be fair, they probably sort of had to be, given the fact that in order to design a programming language and create a compiler for said language, in said language, you'd probably have to be. However, it seems to me that somewhere along the way they forgot (or they didn't know altogether) that a lot of people weren't as adapt F# as they where, which makes this thing a lot less friendly than I think it could be.
Granted, a compiler is a hugely complex piece of software, and it will probably always be hard to get your head around it, but the F# compiler is riddled with HUGE files, with cryptic file names, witch I have no understanding of what does what. In comparison, Roslyn is a piece of beauty. Granted, Roslyn is probably also containing thousands of files, some of which have thousands of lines of code, and are hard to reason about, yet I still find the code-base "clean", and "easy to navigate". When working with the F# compiler I always sit wondering "where should I put this" or "should this be in it's own file" etc. As said, I'm more used to C# than F#, but I still think some fairly major refactoring, renaming of files, and splitting of files are in order.
The compiler services are an afterthought
This was my main quelm with regards to when I started making F# support in vNext. In roslyn, the compiler is a usefull set of APIs that can be linked against, and used by any project, then the compiler is a program written that targets the same APIs, and uses them to compile from the command line. This (amongst other things) ensures that things allways stays in sync.
With F#, the
FSharp.Compiler.Service is mostly a fork of the F# compiler itself, with added APIs on the top. This means that any time the F# compiler is updated, the F# compiler services has to be updated too, to stay in sync. And refactorings, changes to the core of the services are discouraged, as they may introduce differences from the compiler, and make merging harder down the line, which leads to solutions being hacking on top of a compiler that wasn't meant to be used as a service. This can be incredibly painfull. Just to indicate how ridiculous this is, in order to do the compilations I do in F# support for vNext, I create a file-system shim, which I set on a global state object, then I create command line arguments that I would normally use to invoke the command line compiler as a list of strings, and then call the compiler with the arguments. Don't believe me? Look at the code here.
I recently started a "suggestion" over at fslang uservoice to better the compiler API, to which a comment was:
Please add suggestions about FSharp.Compiler.Service at https://github.com/fsharp/FSharp.Compiler.Service/.
The FCS API is being improved all the time - for example recently functionality to take an AST and compile it was added by Nessos. So part of what you want is there. You can certainly implement all of the above as relatively simple improvements and simplifications to the current API.
If you need a hand, then people (myself, Anh-Dung Phan, Tomas Petricek and others like the Nessos guys) are here to help.
BTW adding an FCS-Roslyn bridge would be fabulous
Now, I'm not saying that improving the F# Compiler Services is a bad thing, but I think it's the completely wrong way to go about this. These APIs should be added to the compiler. Not appended on some fork of it that has to be kept in sync manually.
Hopefully this has given you some insight into the state of affairs with regards to F# in vNext applications. I still like F#, and hope that all of the points I've made above get better over time. If nothing else, maybe this blog post can at least increase the awareness that you can indeed use F# in vNext applications, and it works just fine, albeit the tooling is rather lacking. But if you're really hardcore F# fan, you've probably dealt with lacks of tools before :).
Also, I'm looking for someone who has a good understanding of how PDB writing using COM works, so if you're knowledgeable in the field, and are available for some discussion (preferably over some form of VoIP), please leave me a comment bellow, or ping me on JabbR (pinging on JabbR will probably get noticed fastest).
As always, if there's any questions, please do leave a comment :).