Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News F# 6 Introduces Resumable Code, Improvements to Pattern Matching and Tooling, and More

F# 6 Introduces Resumable Code, Improvements to Pattern Matching and Tooling, and More

This item in japanese


F# 6 brings a wealth of new features to the language, library, and tooling aimed at improving performance and making it simpler for programmers wishing to switch to it.

F# 6 adopts a new foundation for concurrency based on resumable code, which is a high-performance way to build asynchronous code or yielding state machines. Resumable code is a new low-level feature of the language that enables defining compositional re-entrant code. This feature is not meant to be used directly through its associated ResumableCode<'Data, 'T> delegate type; rather, it provides the basis for the implementation of new computation expressions.

The first outcome of the introduction of resumable code in F# 6 is the new task {} computation expression, which aims to provide a more performant way of creating an asynchronous task, whereas in F# 5 you would start an async task by executing:

let readFilesTask (path1, path2) =
   async {
        let! bytes1 = File.ReadAllBytesAsync(path1) |> Async.AwaitTask
        let! bytes2 = File.ReadAllBytesAsync(path2) |> Async.AwaitTask
        return Array.append bytes1 bytes2
   } |> Async.StartAsTask

You can now use task and omit the explicit AwaitTask calls:

let readFilesTask (path1, path2) =
   task {
        let! bytes1 = File.ReadAllBytesAsync(path1)
        let! bytes2 = File.ReadAllBytesAsync(path2)
        return Array.append bytes1 bytes2

Besides the simplified syntax, task has much better performance thanks to it relying on a completely different mechanism underneath and ensures improved interoperability with .NET tasks. While in most cases you can safely replace any use of async with task, you should nevertheless pay attention to a number of differences between the two, such as task not implicitly propagating a cancellation token, not supporting asynchronous tail-calls, and so on.

A powerful feature of F# is the capability of defining named partitions to match input data, with the possibility of using those names in matching expressions. This is called Active patterns and F# 6 brings them further with support for struct representation. This basically enables the definition of an active pattern like (A|_) to return a value option instead of an F# regular option value. To make use of these features, you have to use the Struct attribute and replace Some and None with ValueSome and ValueNone, respectively:

[<return: Struct>]
let (|Int|_|) str =
   match System.Int32.TryParse(str) with
   | true, int -> ValueSome(int)
   | _ -> ValueNone

Value options can be useful to speed up your code in many scenarios, although not always.

Another new feature aiming at making the language faster is the new InlineIfLambda attribute that you can use with lambda arguments to indicate they should be inlined at call sites. Inlined lambdas can be particularly convenient, for example, within tight loops.

On the language syntax side, F# 6 does away with the OCaml legacy of using expr.[idx] to index into vectors and adopts the more common expr[idx]. While the old syntax remains valid, the new one is the preferred way of indexing and you can activate a warning (/warnon:3566) to help get rid of the old one.

F# 6 brings many more new features than can be covered here, including implicit integer and upcast conversions, improved debugging, faster compiler, and more. Do no miss the official announcement for the full details.

Rate this Article