Nieuw in F# 4.5

F# 4.5 voegt meerdere verbeteringen toe aan de F#-taal. Veel van deze functies zijn samen toegevoegd om u in staat te stellen efficiënte code te schrijven in F# en tegelijkertijd ervoor te zorgen dat deze code veilig is. Dit betekent het toevoegen van enkele concepten aan de taal en een aanzienlijke hoeveelheid compileranalyse bij het gebruik van deze constructies.

Aan de slag

F# 4.5 is beschikbaar in alle .NET Core-distributies en Visual Studio-hulpprogramma's. Ga aan de slag met F# voor meer informatie.

Span- en byref-achtige structs

Met het Span<T> type dat in .NET Core is geïntroduceerd, kunt u buffers in het geheugen weergeven op een sterk getypte manier. Dit is nu toegestaan in F# vanaf F# 4.5. In het volgende voorbeeld ziet u hoe u een functie die op een Span<T> andere bufferweergave werkt, opnieuw kunt gebruiken:

let safeSum (bytes: Span<byte>) =
    let mutable sum = 0
    for i in 0 .. bytes.Length - 1 do
        sum <- sum + int bytes[i]
    sum

// managed memory
let arrayMemory = Array.zeroCreate<byte>(100)
let arraySpan = new Span<byte>(arrayMemory)

safeSum(arraySpan) |> printfn "res = %d"

// native memory
let nativeMemory = Marshal.AllocHGlobal(100);
let nativeSpan = new Span<byte>(nativeMemory.ToPointer(), 100)

safeSum(nativeSpan) |> printfn "res = %d"
Marshal.FreeHGlobal(nativeMemory)

// stack memory
let mem = NativePtr.stackalloc<byte>(100)
let mem2 = mem |> NativePtr.toVoidPtr
let stackSpan = Span<byte>(mem2, 100)

safeSum(stackSpan) |> printfn "res = %d"

Een belangrijk aspect hiervan is dat Span en andere byref-structs zeer stijve statische analyse hebben uitgevoerd door de compiler die hun gebruik beperken op manieren die u mogelijk onverwacht zult vinden. Dit is de fundamentele afweging tussen prestaties, expressiviteit en veiligheid die wordt geïntroduceerd in F# 4.5.

Vernieuwd doorrefs

Vóór F# 4.5 waren Byrefs in F# onveilig en ondeugdelijk voor talloze toepassingen. Geluidsproblemen rond byrefs zijn opgelost in F# 4.5 en dezelfde statische analyse die is uitgevoerd voor span- en byref-structs zijn ook toegepast.

inref'T<> en outref'T<>

Om het begrip alleen-lezen, alleen-schrijven en beheerde aanwijzer voor lezen/schrijven te vertegenwoordigen, introduceert F# 4.5 de inref<'T>typen outref<'T> die respectievelijk alleen-lezen- en alleen-schrijven-aanwijzers vertegenwoordigen. Elk heeft verschillende semantiek. U kunt bijvoorbeeld niet schrijven naar een inref<'T>:

let f (dt: inref<DateTime>) =
    dt <- DateTime.Now // ERROR - cannot write to an inref!

Standaard leidt type deductie ertoe dat beheerde aanwijzers inref<'T> in overeenstemming zijn met de onveranderbare aard van F#-code, tenzij iets al als veranderlijk is gedeclareerd. Als u iets beschrijfbaar wilt maken, moet u een type declareren als mutable voordat u het adres doorgeeft aan een functie of lid dat het bewerkt. Zie Byrefs voor meer informatie.

Alleen-lezen structs

Vanaf F# 4.5 kunt u aantekeningen toevoegen aan een struct met IsReadOnlyAttribute als zodanig:

[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
    member x.Count1 = count1
    member x.Count2 = count2

Hierdoor kunt u geen onveranderbaar lid in de struct declareren en metagegevens verzenden waarmee F# en C# deze als alleen-lezen kunnen behandelen wanneer ze worden gebruikt vanuit een assembly. Zie ReadOnly-structs voor meer informatie.

Ongeldige aanwijzers

Het voidptr type wordt toegevoegd aan F# 4.5, net als de volgende functies:

  • NativePtr.ofVoidPtr om een ongeldige aanwijzer te converteren naar een systeemeigen aanwijzer
  • NativePtr.toVoidPtr een systeemeigen aanwijzer converteren naar een ongeldige aanwijzer

Dit is handig bij het samenwerken met een systeemeigen onderdeel dat gebruikmaakt van ongeldige aanwijzers.

Het match!-trefwoord

Het match! trefwoord verbetert het patroon dat overeenkomt met het patroon in een berekeningsexpressie:

// Code that returns an asynchronous option
let checkBananaAsync (s: string) =
    async {
        if s = "banana" then
            return Some s
        else
            return None
    }

// Now you can use 'match!'
let funcWithString (s: string) =
    async {
        match! checkBananaAsync s with
        | Some bananaString -> printfn "It's banana!"
        | None -> printfn "%s" s
}

Hiermee kunt u code verkorten waarbij vaak opties (of andere typen) worden gemengd met rekenexpressies zoals asynchroon. Zie match!voor meer informatie.

Ontspannen upcastingvereisten in matrix-, lijst- en reeksexpressies

Het combineren van typen waarbij men van een ander type in matrix-, lijst- en reeksexpressies kan overnemen, heeft traditioneel vereist dat u elk afgeleid type upcastt naar het bovenliggende type met :> of upcast. Dit is nu ontspannen, gedemonstreerd als volgt:

let x0 : obj list  = [ "a" ] // ok pre-F# 4.5
let x1 : obj list  = [ "a"; "b" ] // ok pre-F# 4.5
let x2 : obj list  = [ yield "a" :> obj ] // ok pre-F# 4.5

let x3 : obj list  = [ yield "a" ] // Now ok for F# 4.5, and can replace x2

Ontspanning van inspringing voor matrix- en lijstexpressies

Vóór F# 4.5 moest u matrix- en lijstexpressies overmatig laten inspringen wanneer deze worden doorgegeven als argumenten voor methode-aanroepen. Dit is niet meer vereist:

module NoExcessiveIndenting =
    System.Console.WriteLine(format="{0}", arg = [|
        "hello"
    |])
    System.Console.WriteLine([|
        "hello"
    |])