Co nowego w języku F# 4.5

Język F# 4.5 dodaje wiele ulepszeń języka F#. Wiele z tych funkcji zostało dodanych razem, aby umożliwić pisanie wydajnego kodu w języku F#, a jednocześnie zapewnienie bezpieczeństwa tego kodu. Oznacza to dodanie kilku pojęć do języka i znacznej ilości analizy kompilatora podczas korzystania z tych konstrukcji.

Rozpocznij

Język F# 4.5 jest dostępny we wszystkich dystrybucjach platformy .NET Core i narzędziach programu Visual Studio. Rozpocznij pracę z językiem F# , aby dowiedzieć się więcej.

Struktury podobne do zakresu i byref

Span<T> Typ wprowadzony na platformie .NET Core umożliwia reprezentowanie buforów w pamięci w sposób silnie typizowane, który jest teraz dozwolony w języku F# począwszy od F# 4.5. W poniższym przykładzie pokazano, jak można ponownie użyć funkcji działającej na obiekcie Span<T> z różnymi reprezentacjami buforu:

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"

Ważnym aspektem jest to, że span i inne struktury podobne do byref mają bardzo sztywną analizę statyczną wykonywaną przez kompilator, który ogranicza ich użycie w sposób, który może okazać się nieoczekiwany. Jest to podstawowa kompromis między wydajnością, ekspresyjnością i bezpieczeństwem wprowadzonym w języku F# 4.5.

Przeróbkowane elementy byref

Wcześniej niż F# 4.5, Byrefs w języku F# były niebezpieczne i nie brzmią dla wielu aplikacji. Problemy z dźwiękiem związane z elementami byref zostały rozwiązane w języku F# 4.5 i zastosowano tę samą analizę statyczną wykonywaną dla struktur typu span i byref.

inref'T<> i outref'T<>

Aby przedstawić pojęcie wskaźnika zarządzanego tylko do odczytu, tylko do odczytu i zapisu, język F# 4.5 wprowadza inref<'T>outref<'T> typy , które reprezentują wskaźniki tylko do odczytu i tylko do zapisu. Każda z nich ma różne semantyka. Na przykład nie można zapisać w pliku inref<'T>:

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

Domyślnie wnioskowanie typu będzie wnioskować zarządzane wskaźniki inref<'T> , które mają być zgodne z niezmiennym charakterem kodu języka F#, chyba że coś zostało już zadeklarowane jako modyfikowalne. Aby coś zapisywać, należy zadeklarować typ, tak jak mutable przed przekazaniem jego adresu do funkcji lub elementu członkowskiego, który go manipuluje. Aby dowiedzieć się więcej, zobacz Byrefs.

Readonly, struktury

Począwszy od języka F# 4.5, można dodać adnotację do struktury z następującą IsReadOnlyAttribute :

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

Nie zezwala to na deklarowanie elementu członkowskiego modyfikowalnego w strukturę i emituje metadane, które umożliwiają językom F# i C# traktowanie go jako readonly w przypadku korzystania z zestawu. Aby dowiedzieć się więcej, zobacz ReadOnly, struktury.

Wskaźniki pustki

Typ voidptr jest dodawany do języka F# 4.5, podobnie jak następujące funkcje:

  • NativePtr.ofVoidPtr aby przekonwertować wskaźnik void na natywny wskaźnik int
  • NativePtr.toVoidPtr aby przekonwertować natywny wskaźnik int na wskaźnik void

Jest to przydatne w przypadku współdziałania z natywnym składnikiem, który korzysta ze wskaźników void.

Słowo kluczowe match!

Słowo match! kluczowe zwiększa dopasowanie wzorca, gdy wewnątrz wyrażenia obliczeniowego:

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

Dzięki temu można skrócić kod, który często obejmuje mieszanie opcji (lub innych typów) za pomocą wyrażeń obliczeniowych, takich jak asynchroniczne. Aby dowiedzieć się więcej, zobacz match!.

Złagodzone wymagania dotyczące emisji w wyrażeniach tablicy, listy i sekwencji

Typy mieszania, w których jeden może dziedziczyć z innego wewnątrz tablicy, listy i wyrażeń sekwencji tradycyjnie wymagał odsunąć dowolnego typu pochodnego do jego typu nadrzędnego za pomocą :> lub upcast. Jest to teraz zrelaksowane, pokazane w następujący sposób:

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

Złagodzenie wcięcia dla wyrażeń tablic i list

Przed użyciem języka F# 4.5 trzeba było nadmiernie wcięć tablicę i wyrażenia listy po przekazaniu jako argumentów do wywołań metody. Nie jest to już wymagane:

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