Announcing F# 5 preview 1

Phillip Carter

We’re excited to announce that F# 5 preview 1 is now available! Here’s how to get it:

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

You can use F# 5 preview via the .NET 5 preview SDK, or through the .NET and Jupyter Notebooks support.

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 and add a package reference to the latest FSharp.Core in your project file. It should look something like this:

In future previews, the FSharp.Core reference shouldn’t be required.

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.

F# 5 is focused on better interactive and analytical programming

It’s difficult to come up with a “theme” for a programming language release. Whenever we start a new “cycle” and think about what we’d like to do for the next version of F#, what we have in mind is often very different from what we end up shipping. This is caused by numerous factors: designs don’t work out the way we think they would, things are too difficult to implement at an acceptable level of quality, existing customers report problems that are very expensive and time consuming to fix, etc.

This time, things are different. We started with the intention of improving interactive programming with F#, aligning with the recent investments made to support .NET in Jupyter Notebooks. Interactive programming has historically been a strength of F#, but improving that aspect of F# has been neglected for a few years. With interactive programming becoming increasingly important as machine learning and data science rise in popularity, it was clear that improvements had to be made in the overall experience. Many of these improvements are language changes, and we plan on introducing more features in future preview that are aligned with this.

Not every feature that ultimately ships with F# 5 is targeted specifically to make interactive programming better. F# is a general purpose language after all, and language additions or enhancements have broad use cases that go far beyond a particular style of programming. However, our intention is that the full set of F# 5 features combined makes interactive programming better than it is today. Let’s dive in!

Package references for F# scripts

One of the biggest problems with F# scripts today is incorporating packages. This is now easy to do with the new #r "nuget:..." command:

This will download and install the latest JSON.NET package (if it’s not in your package cache), resolve all dependencies, and let you use the library as if it were in a fully-fledged project.

To run this script (assuming you name it script.fsx) in F# interactive, simply type:

dotnet fsi --langversion:preview script.fsx

Note that the --langversion flag is required. This will not be required once F# 5 is released.

Alternatively, if you’re using Jupyter notebooks, simply execute the cell and it will print the result.

In future previews, we will work to make sure the editor experience in IDEs is in good shape. The focus on this first preview has been in hardening the core mechanism and integrating it with Jupyter Notebooks.

Enhanced slicing

Slicing data is critical when doing analytical work on sets of data. To that end, we enhanced F# slicing in three areas.

Consistent behavior for built-in data types

Today, behavior for slicing the built-in FSharp.Core data types (array, list, string, 2D array, 3D array, 4D array) is not consistent. Some edge-case behavior will throw an exception and some won’t. In F# 5 preview, all built-in types now return empty slices for slices that are impossible to generate:

This change could be controversial to some – the variety of opinions on slicing behavior we’ve seen in other languages has shown that slicing behavior is a hotly debated topic – so we want people to try it out early and let us know how they feel about this change in their own code.

Fixed-index slices for 3D and 4D arrays in FSharp.Core

The built-in 3D and 4D array types have always supported slices, but they did not support fixing a particular index (such as the y-dimension in a 3D array). Now they do!

To illustrate this, consider the following 3D array:

z = 0

x\y 0 1
0 0 1
1 2 3

z = 1

x\y 0 1
0 4 5
1 6 7

What if we wanted to extract the slice [| 4; 5 |] from the array? This is now very simple!

This kind of slice used to not be possible prior to F# 5.

Reverse indexes and slicing from the end

Finally, we have added the concept of a reverse, or “from the end” index. The syntax is ^idx. Here’s how you can an element 1 value from the end of a list:

You can also define reverse indexes for your own types. To do so, you’ll need to implement the following method:

GetReverseIndex: dimension: int -> offset: int

Here’s an example for the Span<'T> type:

We feel that these three enhancements will make slicing data types more convenient in F# 5. What do you think?

The nameof function

The nameof function is a new addition to F#. It’s very useful doing things like logging or validating parameters to functions. Because it uses actual F# symbols instead of string literals, it makes refactoring names over time less difficult.

Consider the following example:

The last line will throw an exception, and the name of the parameter month will be shown in the message.

You can take a name of almost everything in F#, such as:

  • Parameters
  • Functions
  • Classes
  • Modules
  • Namespaces

There are some current restrictions on overloaded methods and type parameters that we are planning on addressing in future previews.

Opening static classes

We’re introducing the ability to “open” a static class as if it were a module or namespace. This applies to any static class in .NET (or any package), or your own F#-defined static class.

There are currently a few unresolved design questions related to shadowing vs. building an overloaded set of methods when combining members from static classes that have the same name. Today, they shadow. That may change in the future. We’re also evaluating if we wish to support opening generic static classes with specific generic substitutions. This kind of change would make the feature very expressive, but it would also be fairly advanced and/or niche.

Applicative computation expressions

Computation expressions (CEs) are used today to model “contextual computations”, or in more FP-friendly terminology, monadic computations. However, they are a more flexible construct than just offering syntax for monads.

F# 5 introduces applicative CEs, which are a slightly different form of CE than what you’re perhaps used to. Applicative CEs allow for significantly more efficient computations provided that every computation is independent, and their results are merely accumulated at the end. When computations are independent of one another, they are also trivially parallelizable. This benefit comes at a restriction, though: computations that depend on previously-computed values are not allowed.

The follow example shows a basic applicative CE for the Result type.

We’re excited to see the clever ways F# programmers will utilize this feature, especially in their own libraries.

If you’re a library author who exposes CEs in their library today, there are some additional considerations you’ll need to be aware of. We recommend that all interested library authors read the summary of new builder methods to determine which to use. We will document these members in the official documentation for CEs once F# 5 is closer to release and the overall design is no longer subject to change.

For consumers of applicative CEs, things aren’t too different from the CEs that you already use. The previously-mentioned restriction around independent computations is the key concept to understand.

The road ahead for F# 5

Despite a number of features being available today, we’re still very much in active development for F# 5. When new features are ready, we’ll release them in the next available .NET 5 preview. If you’re curious about what could be coming next, check out the following links:

Finally, we track all language suggestions in our language suggestions repository. There are quite a lot of suggestions you can learn about, and we encourage you to participate in each discussion.

Because previews are released so that we can get feedback from users, we might make breaking changes from one preview to the next to accommodate feedback we feel deserves a design change. We might also decide to keep a feature in preview for the F# 5 GA release if there is enough feedback that the design isn’t quite right. We encourage you to try these features out and let us know what you feel needs improvement!

Special addendum (2020-03-18): we’re hiring!

Want to help shape F# 5 and future releases? We’re expanding our team in Prague, Czech Republic! If the idea of working on new language and compiler features and improving the overall F# developer experiences sounds interesting, give the job posting a look.

Cheers, and happy hacking!

16 comments

Discussion is closed. Login to edit/delete existing comments.

  • Anthony Lloyd 0

    Is there a possibility indexers can be made consistent with slicers for flexible types i.e. #Gen<‘a>?

    type GenCollection<‘a,’b>(gc:int->Gen<‘a>->Gen<‘b>) =
    member _.GetSlice(s:int option,e:int option) : #Gen<‘a> -> Gen<‘b> =
    let gl = int.GetSlice(s,e)
    fun g -> bind (fun l -> gc l g) gl
    member _.Item
    with get(length:int) = // : #Gen<‘a> -> Gen<‘b> is not possible.
    fun g -> gc length g

    Basically want to:

    Gen.list.[2..100] Gen.float
    Gen.list.[2..100] Gen.float.[0.5..1.0]
    Gen.list.[100] Gen.float
    Gen.list.[100] Gen.float.[0.5..1.0]

    to be able to work but you have to:

    Gen.list.[100] Gen.float.[*] for the third example.

    • Phillip CarterMicrosoft employee 0

      I think this would make for a good language suggestion here: https://github.com/fsharp/fslang-suggestions/

      Another “design hole” in indexing/slicing is generalized, n-dimensional slicing and indexing. This is actually done with the reverse index, which has the dimension parameter. However, similar parameters aren’t in GetSlice and Item (the latter is actually a property, so we’d need a different name). This is something we may look at in future previews, too. So I think your scenario is certainly worth a suggestion and consideration for F# 5.0.

      Could you file a language suggestion that contains the full code and what you’d like to accomplish? Thanks!

      • Anthony Lloyd 0

        Sure, will do. Thanks

  • Tony Henrique 0

    This is very cool!

    In my opinion, now what F# needs to reach more developers is to have these Project Templates in Visual Studio:

    1) For GUI applications (WinUI F# Project template, WPF F# Project template)
    2) Azure Functions F# Project templates.

  • Steve 0

    I think what F# needed most is Higher Kinded Polymorphism (Generics on Generics). We cannot implement Monads conveniently without this.

  • Tobias Burger 0

    Support for dotnet fsi in Visual Studio?

    At the moment the .NET Framework Version of fsi is used for F# Interactive in Visual Studio.
    Are there any plans to support F# Interactive running on the .NET Core runtime?

    • Phillip CarterMicrosoft employee 0

      Hey Tobias,

      We want to support dotnet fsi in Visual Studio. However, it’s unfortunately not straightforward to do just yet.

      Firstly, there are two things to consider: the FSI process itself, and the editing experience with F# scripts. The editing experience is underpinned by a default set of references and APIs you have access to. We want to ensure that you get the right editing experience w.r.t the FSI process you run against. Since dotnet fsi is a .NET Core component, there are different references and APIs when compared to .NET Framework. Unfortunately, there’s no way for the tools to know that a given script is intended to run against .NET Framework or .NET Core, so we can’t dynamically pick the correct set of references and/or launch the correct FSI process when they want to execute code.

      The other thing that complicates this is that Visual Studio is a .NET Framework process, and it requires that all components that load in-process are also .NET Framework or .NET Standard 2.0 components. FSI isn’t a .NET Standard process, so there isn’t a singular component that we can load. So there will need to be two different FSI processes to launch, and the script editing environment will need to probably have some kind of toggle where the user can set which FSI they’re executing against. Certainly possible to build, but there’s no good “pit of success” involved here since it will require that users adjust their environment. The default set of references and APIs for an editor would also be a tricky choice, since no matter which we pick, some existing scripts will load with errors complaining about missing APIs.

      So in summary, we’ll likely find a way to support dotnet fsi, but there isn’t an ideal experience here, since we can’t predict if an arbitrary script is supposed to execute in a particular FSI, and other aspects of Visual Studio means that there will always be a suboptimal experience for this scenario.

  • Jim Horvath 0

    Regarding “nameof”, are there plans to support forward lookup? There are some popular frameworks that use attributes heavily (e.g. DevExpress eXpressApp). A common scenario is to decorate class properties with attributes that take parameters you might often construct from the target property’s name.

    • Phillip CarterMicrosoft employee 0

      Hey Jim, at this time we aren’t planning on supporting forward lookup. A very core aspect of F# design is that things are defined before they are used. This is embedded directly into F# semantics. This also presents an ambiguity problem. Consider the following code:

      type C(x: int) =
          [<DefaultValue>]
          val mutable x: int
      
          [<MyAttribute(nameof x)>]
          member _.M(x: int) = ()

      Currently, x in the nameof refers to the x defined as a mutable field. There’s no way in the language to disambiguate between the x in the primary constructor and the x field in the language, so when we resolve the x name in the attribute, we just pick whatever we saw last. This is consistent with how other parts of the language work. As it stands, there’s no way to inform the nameof call that x refers to the parameter that follows. This might be possible to add as a special rule only for resolving names in nameof, but the downside is that it offers no way for someone to then refer to something that came before! It would also be inconsistent with how the rest of the language works. Given this, we’re unlikely to support forward lookup.

  • Ebresafe Oghenevwogaga 0

    How is this definition of applicative equal to the definition of applicatives in other functional programming languages like Haskell?

  • Mikael Öhman 0

    Nice examples, and nice to get them as a notebook!

    I tested using a modified build of the dotnet interactive image and tried creating a new console app. I get this:

    jovyan@51e38cf1d9d0:~/work/devcontainertest5$ dotnet build
    Microsoft (R) Build Engine version 16.6.0-preview-20112-08+77da97f69 for .NET Core
    Copyright (C) Microsoft Corporation. All rights reserved.
    
      Restore completed in 43.09 ms for /home/jovyan/work/devcontainertest5/devcontainertest5.fsproj.
      You are using a preview version of .NET Core. See: https://aka.ms/dotnet-core-preview
    /home/jovyan/work/devcontainertest5/Program.fs(14,17): error FS0039: The type 'List<T>' does not define the field, constructor or member 'GetReverseIndex'. [/home/jovyan/work/devcontainertest5/devcontainertest5.fsproj]

    when trying the xs.[^1] feature. It works great with dotnet fsi and the extra argument though.

    Is this expected?

    • Phillip CarterMicrosoft employee 0

      Hey Mikael,

      To use preview features in a project, you’ll need to also set the <LangVersion>preview</LangVersion> property in your project file. Hope this helps!

      • Mikael Öhman 0

        Thanks ! Works great.

  • Pavel Voronin 0

    This is a real fun:

    C# alternative:

    var lastTwoOldStyle = xs[(xs.Length-2)..];
    var lastTwoNewStyle = xs[^1..];

    Will provide different results. =)

  • Andreas Vilinski 0

    Is it possible to get package references from custom nuget sources? NuGet.Config or fsi command arguments?

  • Ali O AlShamsi 0

    Love the jupyter notebook support, finally a proper multi core language for data science. Is it?

    Also is F# 5 gonna have winforms visual experience just like C# ? I mean F# only down side is that it has weak gui building experience. Please support winforms visual studio for F# and I mean the drag drop nice visual IDE support.

Feedback usabilla icon