The Ultimate Guide to New Features in F# 6
What’s new in F# 6?
A new feature in F# 6 allows code to optionally indicate that, if an argument is a lambda function, that argument is itself always inlined at call sites.
Design, library, and tools all fall under this category.
The goal of long-term language evolution is to remove any corner cases in the language that might surprise users or create unnecessary barriers to adoption.
Enhancing F#’s performance and interoperability with tasks
F#’s most requested feature – and the most significant technical feature in this release – is to make authoring asynchronous tasks simpler, more performant, and more interoperable with other .NET languages like C#. Creating .NET tasks previously required using async {…} to create a task and then using Async.AwaitTask to execute it. The task {…} can be created and awaited directly using task {…} in F# 6.
let readFilesTask (path1, path2) = task { let! bytes1 = File.ReadAllBytesAsync(path1) let! bytes2 = File.ReadAllBytesAsync(path2) return Array.append bytes1 bytes2 }
F# 5.0 already has task support through the TaskBuilder.fs and Ply libraries. F# 6 was heavily influenced by the authors of these libraries, both directly and indirectly. As well as directly contributing to F# 6, these libraries have also been indirectly contributing to its design. In addition to some differences in namespaces and type inference, some type annotations may be necessary. The built-in support and these libraries implement namespaces and type-inference differently, so some type annotations may be necessary. Using these libraries in F# 6 still works if the correct namespaces are opened in each file and they are explicitly referenced.
The task is very similar to async tasks, both can be used. There are several advantages to using task over async:
-Task performance has improved significantly.
-Stack traces and stepping are better methods for debugging tasks.
-It is easier to interact with .NET packages that expect or produce tasks.
There are a few differences to be aware of if you’re familiar with async:
– As soon as the first asynchronous yield has been reached, the task {…} is executed.
– A cancellation token is not propagated implicitly by task {…}.
– There is no implicit cancellation check in the task {…}.
– Asynchronous tail calls are not supported by task {…}. Use return to accomplish this! .. When no asynchronous yields intervene, recursively may result in stack overflows.
If you are interacting with .NET libraries that use tasks, you should consider using task{…} over async{…} in new code. Before switching to the next task[…], ensure that your code is reviewed if you are relying on the above characteristics of async[…].
We will work with the F# community in the coming months to make available two key optional packages using this feature:
The concept of F# async{…} is re-implemented with recommence code.
The recommence code implements asynchronous sequences asyncSeq{…}.
How expr[idx] simplifies learning F#
F# utilized expr.[idx] as indexing syntax up to and including F# 5.0. For indexed string lookups in OCaml, we used this syntax. 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. F# does not need to diverge in this case.
This isn’t a breaking change; by default, no warnings are issued when expr.[idx] is used. This modification emits some informational messages proposing code clarifications. Further informational messages can be triggered if necessary.
Making F# faster: Partial active pattern structural representations
F# includes the active patterns feature, which allows users to extend pattern matching in intuitive and powerful ways. We’ve enhanced that feature in F# 6 by adding optional struct representations for active patterns. This enables you to constrain a partial active pattern to return a value option by using an attribute:
[<return: Struct>] let (|Int|_|) str = match System.Int32.TryParse(str) with | true, int -> ValueSome(int) | _ -> ValueNone
It is necessary to use the attribute. The code does not change at usage sites. As a result, allocations have been reduced.
To make F# more consistent, use “as” patterns
The right-hand side of an “as” pattern can now be a pattern in F# 6. When a typing test assigns a stronger type to input, this is critical. Consider the following code.
type Pair = Pair of int * int
let analyzeObject (input: obj) = match input with | :? (int * int) as (x, y) -> printfn $"A tuple: {x}, {y}" | :? Pair as Pair (x, y) -> printfn $"A DU: {x}, {y}" | _ -> printfn "N ope" let input = box (1, 2)
Revisions to the indentation syntax in F# to make it more consistent
The F# community has made several significant contributions to make the F# language more consistent in F# 6.
In F# 5.0, for example, the following was permitted:
let c = ( printfn "aaaa" printfn "bbbb" )
The following, however, was not (producing a warning)
Both are permitted in F# 6. F# becomes simpler and easier to understand as a result of this.
Conclusion
In conclusion, F# 6 is a great update for the language that adds a lot of new features and improvements. If you’re a fan of F#, be sure to like, follow and comment on this guide to stay up-to-date on the latest news and features.
References:
https://devblogs.microsoft.com/dotnet/whats-new-in-fsharp-6/