Novità di F# 4.5

F# 4.5 aggiunge più miglioramenti al linguaggio F#. Molte di queste funzionalità sono state aggiunte insieme per consentire di scrivere codice efficiente in F# garantendo al tempo stesso la sicurezza di questo codice. In questo modo si aggiungono alcuni concetti al linguaggio e una quantità significativa di analisi del compilatore quando si usano questi costrutti.

Operazioni preliminari

F# 4.5 è disponibile in tutte le distribuzioni di .NET Core e gli strumenti di Visual Studio. Introduzione a F# per altre informazioni.

Struct simili a intervalli e byref

Il Span<T> tipo introdotto in .NET Core consente di rappresentare i buffer in memoria in modo fortemente tipizzato, che ora è consentito in F# a partire da F# 4.5. Nell'esempio seguente viene illustrato come riutilizzare una funzione che opera su un Span<T> oggetto con rappresentazioni del buffer diverse:

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"

Un aspetto importante è che Span e altri struct simili a byref hanno analisi statiche molto rigide eseguite dal compilatore che limitano l'utilizzo in modi che potrebbero risultare imprevisti. Questo è il compromesso fondamentale tra prestazioni, espressività e sicurezza introdotta in F# 4.5.

Byrefs rinnovati

Prima di F# 4.5, Byrefs in F# non era sicuro e non sono riuscito a creare numerose applicazioni. Sono stati risolti problemi di suono relativi ai byrefs in F# 4.5 e sono state applicate anche le stesse analisi statiche eseguite per gli struct di tipo span e byref.

inref'T<> e outref'T<>

Per rappresentare la nozione di puntatore gestito di sola lettura, di sola scrittura e di lettura/scrittura, F# 4.5 introduce i inref<'T>tipi , outref<'T> che rappresentano rispettivamente puntatori di sola lettura e di sola scrittura. Ognuno ha una semantica diversa. Ad esempio, non è possibile scrivere in un oggetto inref<'T>:

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

Per impostazione predefinita, l'inferenza del tipo dedurrà i puntatori inref<'T> gestiti in linea con la natura non modificabile del codice F#, a meno che qualcosa non sia già stato dichiarato modificabile. Per rendere scrivibile un elemento, è necessario dichiarare un tipo come mutable prima di passare il relativo indirizzo a una funzione o a un membro che lo modifica. Per altre informazioni, vedere Byrefs.

Struct di sola lettura

A partire da F# 4.5, è possibile annotare uno struct con IsReadOnlyAttribute come segue:

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

Ciò impedisce di dichiarare un membro modificabile nello struct e genera metadati che consentono a F# e C# di considerarlo come readonly quando viene usato da un assembly. Per altre informazioni, vedere Struct ReadOnly.

Puntatori void

Il voidptr tipo viene aggiunto a F# 4.5, come sono le funzioni seguenti:

  • NativePtr.ofVoidPtr per convertire un puntatore void in un puntatore int nativo
  • NativePtr.toVoidPtr per convertire un puntatore int nativo in un puntatore void

Ciò è utile quando si interagisce con un componente nativo che usa puntatori void.

Parola chiave match!

La match! parola chiave migliora la corrispondenza dei criteri quando si trova all'interno di un'espressione di calcolo:

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

In questo modo è possibile abbreviare il codice che spesso comporta la combinazione di opzioni (o altri tipi) con espressioni di calcolo, ad esempio asincrone. Per altre informazioni, vedere match!.

Requisiti di upcasting più complessi nelle espressioni di matrice, elenco ed espressione di sequenza

La combinazione di tipi in cui è possibile ereditare da un'altra espressione all'interno di matrici, elenchi ed espressioni di sequenza richiede tradizionalmente di eseguire il upcast di qualsiasi tipo derivato nel tipo padre con :> o upcast. Questo è ora rilassato, dimostrato come segue:

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

Relax del rientro per espressioni di matrice ed elenco

Prima di F# 4.5, è necessario impostare un rientro eccessivo sulle espressioni di matrice ed elenco quando vengono passate come argomenti alle chiamate al metodo. Questo non è più necessario:

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