What's new in F# 6?

05 January 2022 at 10:00 by ParTech Media - Post a comment

F# always had two simple goals - to make the language easier to use for the users and make it more performant. The long-term evolution of this language has eliminated language quirks that used to startle users or created unneeded barriers to adoption. This is applicable to language design, libraries, and other supplementary tools. In F# 6, several new features were introduced that have made the lives of the developers much simpler. Let us understand more about these features in this post.

Table of contents

  1. Introduction
  2. Making F# faster and more interoperable with task {…}
  3. Making F# simpler to learn: indexing with expr[idx]
  4. Making F# simpler and more interoperable: Implicit conversions
  5. Conclusion


F# 6 introduced a host of new features to the language, library, and tooling. This was primarily targeted at boosting performance and making the transition easier for programmers. For instance, it introduced a new concurrency foundation based on resumable code, which is a fast approach to create asynchronous code or to yield state machines. Resumable code is a low-level language feature that allows you to define compositional re-entrant code.

Let’s understand all such features in the subsequent sections.

Making F# faster and more interoperable with task {…}

Making the task of writing asynchronous jobs easier, faster, and more compatible with other.NET languages like C# has been one of the most requested features for F# – and the most significant technical feature in this version. Previously, generating.NET tasks necessitated first creating a task with async and then activating it with Async.AwaitTask.

You can now use task directly to create and await a task in F# 6. Consider the following F# code for creating a .NET-compatible task:

This code can now become:

The built-in task support is available everywhere in F# code – no namespaces are required.

The excellent TaskBuilder.fs and Ply libraries have already provided task support for F# 5.0. These were used to influence the development of the support in F# 6 and supplied a valuable supply of tests.

Both directly and indirectly, the authors of these libraries have made significant contributions to the design of F# 6. It should be simple to migrate code to the built-in support. There are some variations, however, namespaces and type inference differ significantly between the built-in support and these libraries, and some type annotations may be required. If explicitly referenced and the correct namespaces are opened in each file, these community libraries can still be utilized with F# 6.

The use of task {...} is quite similar to the use of async {...}, and both are supported. Using task {...} provides 2 major advantages over using async {...}:

  • Debugging stepping and stack traces for task {…} is better.
  • It's easier to work with.NET packages that are anticipating or producing tasks.

There are certain peculiarities to be aware of if you're familiar with async {...}:

  • task {...} executes the task to the first asynchronous yield immediately
  • task {...} does not convey a cancellation token implicitly
  • task {...} doesn't do any implicit cancellation checks.
  • Asynchronous tail calls are not supported by task {...}.This entails making use of return! If there are no intervening asynchronous yields, recursion may result in stack overflows.

If you're working with.NET libraries that employ tasks, you should use task {…} rather than async {…} in new code. Before switching to task {...}, double-check your code to make sure you're not dependent on the async {…} qualities mentioned above.

The task {…} support for F# 6 is built on the RFC FS-1087 "resumable code" foundation. Resumable code is a key feature that can be utilized to create a wide range of high-performance asynchronous and yielding state machines. F# community is planning to make two essential optional packages available using this feature:

  • Using resumable code, a quick re-implementation of F# async {…}.
  • Using resumable code, a quick re-implementation of asynchronous sequences asyncSeq {...}.

These will be distributed via community packages like FSharp.Control.AsyncSeq and may be included in FSharp.Core in future editions.

Making F# simpler to learn: Indexing with expr[idx]

This syntax was inspired by an OCaml notation for string indexed lookup. Allowing the use of expr[idx] is based on repeated comments from persons learning F# or seeing F# for the first time that dot-notation indexing is an unnecessary deviation from the industry norm.

This isn't a breaking change; by default, no warnings are issued when expr.[idx] is used. However, some informational messages linked to this modification are emitted, proposing code clarifications, and further informational messages can be triggered if desired. For example, to begin reporting uses of the expr.[idx] notation, an optional informational warning (/warnon:3566) can be enabled. In new code, it is advocated using expr[idx] as the indexing syntax on a regular basis.

Making F# simpler and more interoperable: Implicit conversions

F# has enabled support for further "implicit" and "type-directed" conversions in F# 6.

This modification accomplishes three goals:

  • There are fewer explicit upcasts necessary.
  • There are fewer explicit integer conversions needed.
  • Support for implicit conversions in the.NET style has been provided as a first-class feature.


The.NET Foundation, the F# Software Foundation, its members, and other contributors, including Microsoft, worked together to create F#. At all stages of innovation, design, implementation, and delivery, the F# community is involved, the above features are the proof for this.