Life, F# and NativeAOT

Andrii Kurdiumov
5 min readJul 30, 2021
Life as it is

Because most popular features of C# is working, and all small applications in WinForms and console form-factor working for me. I decide to jump into more advanced world of F# which has loyal developer base. This language ecosystem proven to be problematic to NativeAOT. Still despite all shortcoming, state of F# support in NativeAOT much better then in CoreRT, so I think it’s reasonable to try and compile applications and check what’s working and what’s not.

When I wrote this article, I notice that .NET Conf: Focus on F# airing, so that was a happy coincidence. So you will see that even NativeAOT do not forget about F# and trying to improve current story.

Happy beginning

Initially I thought that I will struggle with even simpler application, because sprintf in F#, but to my liking dotnet new console -lang “F#” works like a charm when publish using NativeAOT. Obviously I add Nuget feed and Nuget package. So that raise my appetite for adventure, and I start looking for a target for my compilation efforts.

Path of despair

I decide to try following areas: console apps, desktop apps and web apps. My first attempt was Desktop app. I open GitHub and start looking for any F# project which has UI and interesting example. In comparison to C#, landscape on F# has less interesting UI apps. That does not really stops me, and I found Avalonia.FuncUI. That was excellent choice for me for two reasons: first is that Avalonia works in NativeAOT and UI should works, second is that this library has Game of Life as sample. I cannot dream about better example. So I compile app to NativeAOT. And, and, and… Compilation fails at linker step. I file the issue, and switch to web app.

When looking for web apps, I start with https://fsharp.org/use/web-apps/ where I try to find some web framework which I can torture. Honestly I was confuse by options. Bolero, Fable and Websharper was out of options for me, since they are have their own compiler, and NativeAOT seems to be no use. SAFE stack seems to be interesting, but I cannot understand how I can create something simple. Admittedly that’s not an issue with SAFE, but with me. So I start looking for other options and found Giraffe. When I start compiling sample, I blowup compiler. As you can understand, my exciting mood after compiling Hello World start going straight to the drain.

If I was the person who gave up so easy, I probably never start using NativeAOT, so I continue looking for working cases. I found Flips. Nice library which abstract different solvers, has a lot of dependencies, use Paket. Nice optimization problems. Cool. Let’s start. Add 2 lines to paket.dependencies, like that

Add 1 line to paket.references

and now you can restore packages. Then run dotnet publish to compile. And, and, and… Compiler crashed again.

Dawn of hope

So I have three nice bug reports. So I continue refine them, make some simpler case which reproduce these cases. And by that time (2 days) fix for issue with Avalonia.FuncUI arrive. And what do you think, it compiles, and I have nice Avalonia application, which play Game of life written in F#.

Life as in game

This is something. I would like that all NativeAOT skeptics take a note on this case. Practical issue solved within that project constantly. Small issues just resolved relatively fast. Large work also happens, but it takes time.

Back to Giraffe. That’s still does not working. Probably take some time to make it run, so check in month or so.

So I left with just console app. Fortunately when I try to repro bug, I found that Flips use OPTANE.Modeling, which perform interesting way to support different version of solvers and that’s cause NativeAOT to choke. Once I change expected version of Gurobi solver to 9.1 instead of 9.0 app compiles and even runs :). When I say runs, I mean I add Rd.xml and make printf works in NativeAOT. See how I did that:

So actual issue is not strictly on NativeAOT, but it gives bad impression to the overall theme. This pattern for accessing multiple versions of same dll from single assembly, would not fly in C#+NativeAOT too.

If you familiar with previous state of CoreRT, you may notice that methods required to make printing changed. But principle the same, you have to patiently populate your rd.xml until app starts working. List of all changes to Flips application is here.

After I finish article, I skim over .NET Conf and notice that there some web framework called Suave. So I decide that I should have one more attempt at web workloads with Suave. And it works! I need update to net5.0, add Rd.xml similar to described above, but examples works. So that’s encouraging.

A fly in the ointment

That’s not all. Let’s look at size of applications. Avalonia.FuncUI.GameOfLife produce exe of size 84Mb, that’s bit too much. That’s probably generic expansion in NativeAOT for you. Flips.Examples takes 26Mb on disk which I think acceptable, given that I do not even try to squeeze anything out of it. Suave example was 32Mb, also not bad.

Almost forget about that one. Printing discriminated unions have issues. Be aware.

Summary

F# support in NativeAOT so far can be much better. My gut feeling that things become better, but you have harder time make you app smaller. Anyway I think you can write Desktop apps, console apps, and web apps in F# and compile using NativeAOT. That’s achievement in itself in my opinion.

Call to action

Dear reader, since I’m mostly C# developer, I may choose libraries from F# world incorrectly. If by a chance you are F# developer by trade, please point me to other common libraries which I can test.

I know that’s hard to ask given all what I wrote today, but please try and compile your apps using NativeAOT. Would be good to have more data points.

--

--

Andrii Kurdiumov

Math lover, lost in the woods of software development