Nyheter i F# 4.5

F# 4.5 lägger till flera förbättringar av F#-språket. Många av dessa funktioner har lagts till tillsammans så att du kan skriva effektiv kod i F# samtidigt som du ser till att koden är säker. Det innebär att lägga till några begrepp i språket och en betydande mängd kompilatoranalys när du använder dessa konstruktioner.

Kom igång

F# 4.5 finns i alla .NET Core-distributioner och Visual Studio-verktyg. Kom igång med F# om du vill veta mer.

Span- och byref-liknande structs

Med Span<T> den typ som introducerades i .NET Core kan du representera buffertar i minnet på ett starkt skrivet sätt, vilket nu är tillåtet i F# från och med F# 4.5. I följande exempel visas hur du kan återanvända en funktion som fungerar på en Span<T> med olika buffertrepresentationer:

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"

En viktig aspekt av detta är att Span och andra byref-liknande structs har mycket stel statisk analys som utförs av kompilatorn som begränsar deras användning på ett sätt som du kanske tycker är oväntat. Detta är den grundläggande kompromissen mellan prestanda, uttryckskraft och säkerhet som introduceras i F# 4.5.

Förnyad byrefs

Före F# 4.5 var Byrefs i F# osäkra och osunda för många program. Ljudproblem kring byrefs har åtgärdats i F# 4.5 och samma statiska analys som gjorts för span- och byref-liknande structs tillämpades också.

inref'T<> och outref'T<>

För att representera begreppet skrivskyddad, skrivskyddad och skriv-/skrivhanterad pekare introducerar inref<'T>outref<'T> F# 4.5 , typerna för att representera skrivskyddade respektive skrivskyddade pekare. Var och en har olika semantik. Du kan till exempel inte skriva till en inref<'T>:

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

Som standard kommer typinferens att härleda hanterade pekare så inref<'T> att de överensstämmer med F#-kodens oföränderliga karaktär, såvida inte något redan har deklarerats som föränderligt. För att göra något skrivbart måste du deklarera en typ som mutable innan du skickar dess adress till en funktion eller medlem som manipulerar den. Mer information finns i Byrefs.

Readonly structs

Från och med F# 4.5 kan du kommentera en struct med IsReadOnlyAttribute så här:

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

Detta gör att du inte kan deklarera en föränderlig medlem i structen och genererar metadata som gör att F# och C# kan behandla det som skrivskyddat när de används från en sammansättning. Mer information finns i ReadOnly structs.

Tomrumspekare

Typen voidptr läggs till i F# 4.5, liksom följande funktioner:

  • NativePtr.ofVoidPtr konvertera en tomrumspekare till en intern int-pekare
  • NativePtr.toVoidPtr om du vill konvertera en intern int-pekare till en tomrumspekare

Detta är användbart när du samverkar med en inbyggd komponent som använder tomrumspekare.

Nyckelordet match!

Nyckelordet match! förbättrar mönstermatchningen i ett beräkningsuttryck:

// 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
}

På så sätt kan du förkorta kod som ofta omfattar blandningsalternativ (eller andra typer) med beräkningsuttryck som asynkronisering. Mer information finns i match!.

Avslappnade uppcastingskrav i matris-, list- och sekvensuttryck

Blandningstyper där man kan ärva från en annan i matris-, list- och sekvensuttryck har traditionellt krävt att du genererar en härledd typ till dess överordnade typ med :> eller upcast. Detta är nu avslappnad, vilket visas på följande sätt:

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

Indragsavslappning för matris- och listuttryck

Innan F# 4.5 behövde du dra in matris- och listuttryck för mycket när de skickades som argument till metodanrop. Detta krävs inte längre:

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