Now available as a preview, F# 4.5 introduces a number of new features, including support for .NET Core 2.1 new primitive type Span<T>
, a new Match!
keyword, and more.
Spans aim to make low-level code in the style of pointer manipulation safe and predictable, thus improving memory-efficiency and performance by eliminating the need for memory allocation in a number of cases. To this aim a Span provides a virtual view of data that is already stored somewhere in memory. For example, if you have a 10,000 element array, you can virtually create a slice containing its first 1,000 elements and pass it to a function without making a copy of those elements:
let nativeMemory = Marshal.AllocHGlobal(100);
let nativeSpan = new Span<byte>(nativeMemory.ToPointer(), 100)
let nativeSpanSlice = new Span<byte>(nativeMemory.ToPointer(), 10)
let mem = NativePtr.stackalloc<byte>(100)
let mem2 = mem |> NativePtr.toVoidPtr
let stackSpan = Span<byte>(mem2, 100)
Actually, the Span feature includes a number of sub-features, such as the voidptr
type, the NativePtr.ofVoidPtr
and NativePtr.toVoidPtr
functions and others that bring F# 4.5 Span support on a par with C# 7.3’s, as the following correspondence table shows:
C# F#
out int arg arg: byref<int>
out int arg arg: outref<int>
in int arg arg: inref<int>
ref readonly int Inferred or arg: inref<int>
ref expr &expr
To ensure code soundness, F# imposes a number of restrictions on the use of Spans, which apply to all byref
-like structs:
- A
let
-bound value cannot have its reference escape the scope it was defined in. byref
-like structs cannot be instance or static members of a class or normal struct.byref
-like structs cannot be used as a generic type parameter.byref
-like structs cannot by captured by any closure construct.
The Match!
keyword has been long awaited by developers to simplify matching syntax inside computation expressions. In F# 4.1, you were required to use let!
as an intermediate step prior to matching:
let funcWithString (s: string) =
async {
let! r = asyncFunction s
match r with
| Some bananaString -> printfn "It's banana!"
| None -> printfn "%s" s
}
With F# 4.5, you can just write:
let funcWithString (s: string) =
async {
match! asyncFunction s with
| Some bananaString -> printfn "It's banana!"
| None -> printfn "%s" s
}
Other features in F# 4.5 include the following:
- No upcast is required anymore when using
yield
in sequences, lists, and arrays:
let x2 : obj list = [ yield "a" :> obj ] // pre-F# 4.5
let x3 : obj list = [ yield "a" ] // F# 4.5
-
Enumeration cases are emitted as public by default to help with profiling tools.
-
Async stack traces have been improved and provide more meaningful information.
According to Carter, despite being a preview, F# 4.5 is very stable and will be included in the upcoming release of Visual Studio 2017 update 15.8. In the meantime, it can be obtained manually both for .NET core platforms and Windows.