Ler em inglês

Partilhar via


O que há de novo no F# 4.5

O F# 4.5 adiciona várias melhorias à linguagem F#. Muitos desses recursos foram adicionados para permitir que você escreva código eficiente em F# e, ao mesmo tempo, garanta que esse código seja seguro. Fazer isso significa adicionar alguns conceitos à linguagem e uma quantidade significativa de análise do compilador ao usar essas construções.

Começar agora

O F# 4.5 está disponível em todas as distribuições do .NET Core e nas ferramentas do Visual Studio. Comece a usar o F# para saber mais.

Estruturas semelhantes a span e byref

O Span<T> tipo introduzido no .NET Core permite que você represente buffers na memória de uma maneira fortemente tipada, o que agora é permitido em F# começando com F# 4.5. O exemplo a seguir mostra como você pode reutilizar uma função que opera em um Span<T> com diferentes representações de buffer:

F#
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"

Um aspeto importante para isso é que Span e outras estruturas semelhantes a byref têm análises estáticas muito rígidas realizadas pelo compilador que restringem seu uso de maneiras que você pode achar inesperadas. Este é o compromisso fundamental entre desempenho, expressividade e segurança que é introduzido no F# 4.5.

Byrefs renovados

Antes do F# 4.5, Byrefs em F# eram inseguros e insalubres para inúmeras aplicações. Problemas de solidez em torno de byrefs foram abordados em F# 4.5 e a mesma análise estática feita para span e byref-like structs também foi aplicada.

inref'T<> e outref'T<>

Para representar a noção de um ponteiro gerenciado somente leitura, somente gravação e leitura/gravação, o F# 4.5 introduz os inref<'T>tipos , outref<'T> para representar ponteiros somente leitura e somente gravação, respectivamente. Cada um tem semânticas diferentes. Por exemplo, não é possível escrever em um inref<'T>arquivo :

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

Por padrão, a inferência de tipo inferirá ponteiros gerenciados como inref<'T> estando de acordo com a natureza imutável do código F#, a menos que algo já tenha sido declarado como mutável. Para tornar algo gravável, você precisará declarar um tipo como mutable antes de passar seu endereço para uma função ou membro que o manipule. Para saber mais, consulte Byrefs.

Estruturas somente leitura

A partir do F# 4.5, você pode anotar uma struct com IsReadOnlyAttribute :

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

Isso impede que você declare um membro mutável no struct e emite metadados que permitem que F# e C# o tratem como somente leitura quando consumido de um assembly. Para saber mais, consulte Estruturas ReadOnly.

Ponteiros vazios

O voidptr tipo é adicionado ao F# 4.5, assim como as seguintes funções:

  • NativePtr.ofVoidPtr Para converter um ponteiro void em um ponteiro int nativo
  • NativePtr.toVoidPtr Para converter um ponteiro int nativo em um ponteiro void

Isso é útil ao interoperar com um componente nativo que faz uso de ponteiros vazios.

A palavra-chave match!

A match! palavra-chave melhora a correspondência de padrões quando está dentro de uma expressão computacional:

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

Isso permite encurtar o código que geralmente envolve a mistura de opções (ou outros tipos) com expressões de computação, como assíncrono. Para saber mais, consulte match!.

Requisitos de upcasting relaxados em expressões de matriz, lista e sequência

A mistura de tipos em que um pode herdar de outro dentro de expressões de matriz, lista e sequência tradicionalmente exige que você atualize qualquer tipo derivado para seu tipo pai com :> ou upcast. Isto é agora relaxado, demonstrado da seguinte forma:

F#
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

Relaxamento de recuo para expressões de matriz e lista

Antes do F# 4.5, você precisava recuar excessivamente a matriz e listar expressões quando passadas como argumentos para chamadas de método. Isso não é mais necessário:

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