F# 5 and F# tools update for June
We’re excited to announce more updates to F# 5 today! We shipped an initial set of features with F# 5 preview 1, and they have all been stabilizing since that release including a updates from last month. Today, we’re happy to announce some new language features, a sneak peek at using F# in VSCode notebooks, and some F# tooling updates that will align with Visual Studio 2019 Update 16.7.
You can get the latest F# 5 in these ways
- Install the latest .NET 5 preview SDK
- Install .NET for Jupyter/nteract
- Install .NET for VSCode Notebooks
If you’re using Visual Studio on Windows, you’ll need both the .NET 5 preview SDK and Visual Studio Preview installed.
Using F# 5 preview
If you’re using the .NET 5 preview SDK, check out a sample repository showing off some of what you can do with F# 5. You can play with each of the features there instead of starting from scratch.
If you’d rather use F# 5 in your own project, you’ll need to add a
LangVersion property with
preview as the value. It should look something like this:
Alternatively, if you’re using Jupyter Notebooks and want a more interactive experience, check out a sample notebook that shows the same features, but has a more interactive output.
Improvements to nuget references for F# scripts
#r "nuget:..." has now been enhanced to support packages that pull in native dependencies. Prior to this update, some packages weren’t 100% usable if they needed to call into certain kinds of native code. This is now resolved. The following is an example of the Flips library, which used to fail on the second-to-last line of code where it serialized a model. Now it works!
Additionally, we now support referencing packages where the order of
.dll references being passed to a compiler matters, such as FParsec. The following script now works:
This is also a significant improvement over the “old” way to use a package like this, where you needed to manually ensure the ordering of the
.dlls being passed to the compiler to be able to use it in scripts.
Better interop with nullable value types
Nullable (value) types (called Nullable Types historically) have long been supported by F#, but interacting with them has traditionally been somewhat of a pain since you’d have to construct a
Nullable<SomeType> wrapper every time you wanted to pass a value. Now the compiler will implicitly convert a value type into a
Nullable<ThatValueType> if the target type matches. The following code is now possible:
Improved stack traces in F# async and other computation expressions
Thanks to a contribution by Nino Floris, stack traces coming from caught exceptions in computation expressions (such as F# async) now retain more information. Consider the following code that uses the Ply library:
Prior to this change, the
origin function would not appear in stack traces without a workaround in the Ply library (and any other library where this is a scenario). Now it shows the full trace:
What’s coming next
There are some more immediate updates we’re making that we hope to release in the next set of .NET 5 previews, now that we’ve resolved some longstanding design issues. There isn’t a way to use these changes in .NET 5 yet, though some may appear in Jupyter/VSCode notebooks before the next .NET 5 release where they can be used anywhere.
F# quotations improvements
We’re finishing up a fundamental improvement to F# Code Quotations, a metaprogramming feature that lets you generate and manipulate an abstract syntax tree that represents the F# code.
Although powerful, F# Code Quotations have had a severe deficiency up until this point: they didn’t carry “trait calls” to sufficiently represent the actual semantics of the code being “quoted” if it relied on type constraints. A common way this could manifest itself was “allowing” code with arithmetic that would merely throw an exception at runtime.
These enhancements are particularly relevant to translating F# code to run on other runtimes like PyTorch or ONNX, a key scenario we’re exploring as a means to attract developers in more “analytical” domains to F# and .NET. We also anticipate numerous smaller issues that F# developers using F# Code Quotations today had to work around to be resolved, with the code they expect to “just work” actually live up to that phrase.
A trivial example of code that now just works is as follows:
This used to throw an exception. It now emits
-1 as you would expect it to.
We’re also collaborating with the Fable maintainers to ensure the underlying mechanism used to preserve constraints surfaces in the F# Compiler Service so that tools like Fable, WebSharper, etc. can accurately represent this sort of code without their own workarounds.
To read more about this feature (warning: there is a lot to read about), you can check out the RFC that goes over the design in detail.
Finishing up nameof
We’re finishing up our design changes for the
nameof function and arrived at the following improvements:
nameofon an operator to generate the symbol in source rather than the compiled name of the operator
- Allowing taking the name of generic type parameters
The last point was tricky from a design standpoint, and results in
nameof actually having two forms:
This second form for generic type parameters aligns
nameof with the
typedefof intrinsic functions that require a similar form. Here’s what the changes look like in source code:
Open type declarations
We’re renaming the feature “open static classes” to “open type declarations”. This is because we’re making the following major changes:
- Syntax is now
open type SomeType(old syntax was
- You can now open any type, not just a static class like before, and expose static members and constants contained within it
Code will now look like this:
Allow implementing the same interface at different generic instantiations
As another example of F# open source community excellence, Lukas Rieger contributed an initial design and implementation of this feature. In a future F# 5 preview, code like this will be able to compile:
F# and VSCode notebooks
We’re really excited to share the first preview of F# support for VSCode notebooks. It uses the same kernel that powers F# support in Jupyter, except it plugs into a different system that enables a richer overall experience.
Here’s what it looks like when executing one of the code samples in this blog post:
It also supports inline charts (in this case via the XPlot.plotly package):
And you can import/export Jupyter notebooks:
It’s an early preview, but it supports some great features already
- Preliminary language service support
- Inline charting and formatting of data
- Compact data format (
.dib) that makes code review easy
- Ability to import Jupyter notebooks (
.ipynb) and convert to a
- Ability to export a
.dibnotebook as a Jupyter notebook (
Some features on the more immediate roadmap include:
- Better IntelliSense
- Sharing F#-defined values with C# cells
We’d love to have you try it out and give us feedback on what you feel needs to be there. To do so, follow the installation instructions.
A great way to get started is to download the F# 5 Jupyter notebook and convert it like so:
- Download the file into a folder or existing workspace
- Open the VSCode command palette
- Type in
- Select the option to import a Jupyter notebook
- Name the new file
If something seems off, doesn’t work right, or a feature is missing, don’t be shy and file an issue on GitHub! The team wants your feedback.
.NET Framework projects default to SDK-style project files
Starting with the Visual Studio 16.7 update, we’ve deprecated the older “long-form” F# projects. They will still load in Visual Studio today, but any new projects you create will only be .NET SDK-style moving forward. The project files now look like this:
Keyword descriptions now show in completion lists:
Extended completion (showing completion for unimported types, an optional feature) now uses the same built-in UI that C# does. The completion window will dynamically resize depending on the size of the namespace to open:
When typing at the top of a file (such as in an F# script), using a code fixer via IntelliSense to add a necessary
open declaration will now work correctly.
More performance improvements for Visual Studio tooling
The performance work for larger codebases is always ongoing, focused primarily on elminating some unnecessary memory usage over time. Steffen Forkmann also helped in this effort with some improvements.
Additionally, Saul Rennison contributed an improvement to CPU time spend building F# projects at design-time in Visual Studio, a process that happens ambiently and many times over a session. The bottleneck identified has had its CPU time reduced by ~90%. If you have a lot of F# projects in a solution, you may notice that various things feel “quicker” than before.
Thank you to everyone who has contributed! F# is continually improving because of your work.
The continuing F# 5 journey
We’re still not done with F# 5, and aside from what I mentioned earlier about what’s next, we’ll be focused on these three things:
- Continuing inclusion of language features when their design and implementations are stable
- Continuing to improve the tooling performance for larger F# codebases
- Making F# in Jupyter and Visual Studio Code Notebooks the best language for data science and analytical work
Later this year we will “close down” on F# 5, marking a period of time where we focus on stabilization and planning for the next F# language version. We don’t have a date in mind for that year, but we’re thinking it will be near the end of the Summer. Until that point, If you’d like to follow along on a much more detailed level, you can check out the F# development repository. We’re tracking the work we’re focused on with a GitHub issue roughly each month, and we encourage you all to provide input to the list of things and let us know what you think.
Cheers, and happy F# coding!