5

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

 2 years ago
source link: https://www.infoq.com/news/2021/10/fsharp-6-resumable-code/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

Oct 27, 2021 2 min read

F# 6 brings a wealth of new features to the language, library, and tooling aimed to improve performance and make 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 to create 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 this 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 detail.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK