Co nowego w języku F# 6

Język F# 6 dodaje kilka ulepszeń języka F# i F# Interactive. Jest on udostępniany za pomocą platformy .NET 6.

Najnowszy zestaw .NET SDK można pobrać ze strony pobierania platformy .NET.

Rozpocznij

Język F# 6 jest dostępny we wszystkich dystrybucjach platformy .NET Core i narzędziach programu Visual Studio. Aby uzyskać więcej informacji, zobacz Wprowadzenie do języka F#.

zadanie {...}

Język F# 6 obejmuje natywną obsługę tworzenia zadań platformy .NET w kodzie języka F#. Rozważmy na przykład następujący kod języka F#, aby utworzyć element . Zadanie zgodne z platformą NET:

let readFilesTask (path1, path2) =
   async {
        let! bytes1 = File.ReadAllBytesAsync(path1) |> Async.AwaitTask
        let! bytes2 = File.ReadAllBytesAsync(path2) |> Async.AwaitTask
        return Array.append bytes1 bytes2
   } |> Async.StartAsTask

Przy użyciu języka F# 6 ten kod można napisać ponownie w następujący sposób.

let readFilesTask (path1, path2) =
   task {
        let! bytes1 = File.ReadAllBytesAsync(path1)
        let! bytes2 = File.ReadAllBytesAsync(path2)
        return Array.append bytes1 bytes2
   }

Obsługa zadań była dostępna dla języka F# 5 za pośrednictwem doskonałych bibliotek TaskBuilder.fs i Ply. Migracja kodu do wbudowanej obsługi powinna być prosta. Istnieją jednak pewne różnice: przestrzenie nazw i wnioskowanie typów różnią się nieco między wbudowaną obsługą a tymi bibliotekami, a niektóre dodatkowe adnotacje typu mogą być potrzebne. W razie potrzeby nadal można używać tych bibliotek społeczności z językiem F# 6, jeśli odwołujesz się do nich jawnie i otwierasz poprawne przestrzenie nazw w każdym pliku.

Użycie task {…} metody jest bardzo podobne do używania polecenia async {…}. Korzystanie z programu ma kilka zalet w porównaniu z task {…} usługą async {…}:

  • Obciążenie task {...} jest niższe, prawdopodobnie poprawia wydajność w ścieżkach kodu gorącego, w których praca asynchroniczna jest wykonywana szybko.
  • Debugowanie kroków i śladów stosu jest task {…} lepsze.
  • Współpraca z pakietami .NET, które oczekują lub tworzą zadania, jest łatwiejsza.

Jeśli znasz program async {…}, istnieją pewne różnice, o których należy pamiętać:

  • task {…} natychmiast wykonuje zadanie do pierwszego punktu oczekiwania.
  • task {…} nie propaguje niejawnie tokenu anulowania.
  • task {…} nie wykonuje niejawnych testów anulowania.
  • task {…} nie obsługuje asynchronicznych tailcalls. Oznacza to, że użycie return! .. rekursywnie może spowodować przepełnienie stosu, jeśli nie ma pośredniczącego oczekiwania asynchronicznego.

Ogólnie rzecz biorąc, należy rozważyć użycie w task {…}async {…} nowym kodzie, jeśli pracujesz z bibliotekami platformy .NET korzystającymi z zadań, a jeśli nie korzystasz z asynchronicznych odwołań kodu tailcalls lub niejawnego propagacji tokenu anulowania. W istniejącym kodzie należy przełączyć się tylko na task {…} po przejrzeniu kodu, aby upewnić się, że nie korzystasz z wcześniej wymienionych cech async {…}.

Ta funkcja implementuje F# RFC FS-1097.

Prostsza składnia indeksowania za pomocą polecenia expr[idx]

Język F# 6 umożliwia składnię expr[idx] indeksowania i fragmentowania kolekcji.

Do i włącznie z F# 5 język F# został użyty expr.[idx] jako składnia indeksowania. Zezwalanie na korzystanie z elementu expr[idx] opiera się na powtarzających się opiniach z tych uczenia się języka F# lub obejrzeniu języka F# po raz pierwszy, że użycie indeksowania kropkami występuje jako niepotrzebne rozbieżność ze standardowej praktyki branżowej.

Nie jest to zmiana powodująca niezgodność, ponieważ domyślnie nie są emitowane żadne ostrzeżenia dotyczące użycia elementu expr.[idx]. Jednak niektóre komunikaty informacyjne sugerujące wyjaśnienie kodu są emitowane. Opcjonalnie możesz również aktywować dalsze komunikaty informacyjne. Możesz na przykład aktywować opcjonalne ostrzeżenie informacyjne (/warnon:3566), aby rozpocząć raportowanie użycia expr.[idx] notacji. Aby uzyskać więcej informacji, zobacz Notacja indeksatora.

W nowym kodzie zalecamy systematyczne użycie expr[idx] metody jako składni indeksowania.

Ta funkcja implementuje F# RFC FS-1110.

Reprezentacje struktury dla częściowo aktywnych wzorców

Język F# 6 rozszerza funkcję "aktywne wzorce" z opcjonalnymi reprezentacjami struktury dla częściowych aktywnych wzorców. Dzięki temu można użyć atrybutu do ograniczenia częściowego aktywnego wzorca w celu zwrócenia opcji wartości:

[<return: Struct>]
let (|Int|_|) str =
   match System.Int32.TryParse(str) with
   | true, int -> ValueSome(int)
   | _ -> ValueNone

Użycie atrybutu jest wymagane. W witrynach użycia kod nie zmienia się. Wynikiem netto jest zmniejszenie alokacji.

Ta funkcja implementuje F# RFC FS-1039.

Przeciążone operacje niestandardowe w wyrażeniach obliczeniowych

Język F# 6 umożliwia używanie atrybutu CustomOperationAttribute w przeciążonych metodach.

Rozważ następujące użycie konstruktora contentwyrażeń obliczeniowych:

let mem = new System.IO.MemoryStream("Stream"B)
let content = ContentBuilder()
let ceResult =
    content {
        body "Name"
        body (ArraySegment<_>("Email"B, 0, 5))
        body "Password"B 2 4
        body "BYTES"B
        body mem
        body "Description" "of" "content"
    }

W tym przypadku operacja niestandardowa body przyjmuje różną liczbę argumentów różnych typów. Jest to obsługiwane przez implementację następującego konstruktora, który używa przeciążenia:

type Content = ArraySegment<byte> list

type ContentBuilder() =
    member _.Run(c: Content) =
        let crlf = "\r\n"B
        [|for part in List.rev c do
            yield! part.Array[part.Offset..(part.Count+part.Offset-1)]
            yield! crlf |]

    member _.Yield(_) = []

    [<CustomOperation("body")>]
    member _.Body(c: Content, segment: ArraySegment<byte>) =
        segment::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, bytes: byte[]) =
        ArraySegment<byte>(bytes, 0, bytes.Length)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, bytes: byte[], offset, count) =
        ArraySegment<byte>(bytes, offset, count)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, content: System.IO.Stream) =
        let mem = new System.IO.MemoryStream()
        content.CopyTo(mem)
        let bytes = mem.ToArray()
        ArraySegment<byte>(bytes, 0, bytes.Length)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, [<ParamArray>] contents: string[]) =
        List.rev [for c in contents -> let b = Text.Encoding.ASCII.GetBytes c in ArraySegment<_>(b,0,b.Length)] @ c

Ta funkcja implementuje F# RFC FS-1056.

Wzorce "as"

W języku F# 6 prawa strona as wzorca może teraz być wzorcem. Jest to ważne, gdy test typu dał silniejszy typ danych wejściowych. Rozważmy na przykład następujący kod:

type Pair = Pair of int * int

let analyzeObject (input: obj) =
    match input with
    | :? (int * int) as (x, y) -> printfn $"A tuple: {x}, {y}"
    | :? Pair as Pair (x, y) -> printfn $"A DU: {x}, {y}"
    | _ -> printfn "Nope"

let input = box (1, 2)

W każdym przypadku wzorca obiekt wejściowy jest testowany według typu. Prawa strona as wzorca może być teraz kolejnym wzorcem, który może być zgodny z obiektem w silniejszym typie.

Ta funkcja implementuje F# RFC FS-1105.

Poprawki składni wcięcia

Język F# 6 usuwa szereg niespójności i ograniczeń dotyczących używania składni obsługującej wcięcia. Zobacz RFC FS-1108. Rozwiązuje to 10 znaczących problemów wyróżnionych przez użytkowników języka F# od wersji F# 4.0.

Na przykład w języku F# 5 dozwolony był następujący kod:

let c = (
    printfn "aaaa"
    printfn "bbbb"
)

Jednak następujący kod nie został dozwolony (wygenerował ostrzeżenie):

let c = [
    1
    2
]

W języku F# 6 oba są dozwolone. Dzięki temu język F# jest prostszy i łatwiejszy do nauki. Współautor społeczności języka F# Hadrian Tang prowadził tę drogę, w tym niezwykłe i wysoce cenne systematyczne testowanie funkcji.

Ta funkcja implementuje F# RFC FS-1108.

Dodatkowe niejawne konwersje

W języku F# 6 aktywowano obsługę dodatkowych konwersji "niejawnych" i "kierowanych przez typ", zgodnie z opisem w artykule RFC FS-1093.

Ta zmiana ma trzy zalety:

  1. Wymagana jest mniejsza liczba jawnych emisji
  2. Wymagana jest mniejsza liczba jawnych konwersji liczb całkowitych
  3. Obsługa najwyższej klasy dla programu . Dodano niejawne konwersje w stylu NET

Ta funkcja implementuje F# RFC FS-1093.

Dodatkowe niejawne konwersje emisji upcast

Język F# 6 implementuje dodatkowe niejawne konwersje emisji upcast. Na przykład w języku F# 5 i starszych wersjach do wyrażenia zwrotnego były potrzebne emisje upcast podczas implementowania funkcji, w której wyrażenia miały różne podtypy w różnych gałęziach, nawet wtedy, gdy była obecna adnotacja typu. Rozważmy następujący kod języka F# 5:

open System
open System.IO

let findInputSource () : TextReader =
    if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
        // On Monday a TextReader
        Console.In
    else
        // On other days a StreamReader
        File.OpenText("path.txt") :> TextReader

W tym miejscu gałęzie obliczeń warunkowych odpowiednio i TextReaderStreamReader , a upcast został dodany, aby obie gałęzie miały typ StreamReader. W języku F# 6 te emisje są teraz dodawane automatycznie. Oznacza to, że kod jest prostszy:

let findInputSource () : TextReader =
    if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
        // On Monday a TextReader
        Console.In
    else
        // On other days a StreamReader
        File.OpenText("path.txt")

Opcjonalnie można włączyć ostrzeżenie, aby wyświetlić ostrzeżenie /warnon:3388 w każdym momencie, w którym jest używana dodatkowa niejawna emisja upcast, zgodnie z opisem w temacie Opcjonalne ostrzeżenia dotyczące niejawnych konwersji.

Niejawne konwersje liczb całkowitych

W języku F# 6 32-bitowe liczby całkowite są rozszerzane do 64-bitowych liczb całkowitych, gdy oba typy są znane. Rozważmy na przykład typowy kształt interfejsu API:

type Tensor(…) =
    static member Create(sizes: seq<int64>) = Tensor(…)

W języku F# 5 należy użyć literałów liczb całkowitych dla int64:

Tensor.Create([100L; 10L; 10L])

lub

Tensor.Create([int64 100; int64 10; int64 10])

W języku F# 6 rozszerzenie odbywa się automatycznie dla int32 elementu do int64, int32 do nativeinti int32 do double, gdy zarówno typ źródłowy, jak i docelowy są znane podczas wnioskowania typu. W takich przypadkach, jak w poprzednich przykładach, int32 można użyć literałów:

Tensor.Create([100; 10; 10])

Pomimo tej zmiany język F# nadal używa jawnego rozszerzania typów liczbowych w większości przypadków. Na przykład rozszerzenie niejawne nie ma zastosowania do innych typów liczbowych, takich jak int8 lub int16, lub z float32 do float64, albo, gdy typ źródłowy lub docelowy jest nieznany. Możesz również opcjonalnie włączyć ostrzeżenie, aby wyświetlić ostrzeżenie /warnon:3389 w każdym punkcie niejawne rozszerzenie liczbowe jest używane zgodnie z opisem w temacie Opcjonalne ostrzeżenia dotyczące niejawnych konwersji.

Obsługa najwyższej klasy dla programu . Konwersje niejawne w stylu NET

W języku F# 6 konwersje "op_Implicit" platformy .NET są stosowane automatycznie w kodzie języka F# podczas wywoływania metod. Na przykład w języku F# 5 konieczne było użycie XName.op_Implicit podczas pracy z interfejsami API platformy .NET dla kodu XML:

open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")

W języku F# 6 konwersje są stosowane automatycznie dla wyrażeń argumentów, op_Implicit gdy typy są dostępne dla wyrażenia źródłowego i typu docelowego:

open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")

Opcjonalnie można włączyć ostrzeżenie, aby wyświetlić ostrzeżenie /warnon:3395 w każdej konwersji punktów op_Implicit rozszerzania jest używana w argumentach metody, zgodnie z opisem w temacie Opcjonalne ostrzeżenia dotyczące niejawnych konwersji.

Uwaga

W pierwszej wersji języka F# 6 ten numer ostrzeżenia to /warnon:3390. Z powodu konfliktu numer ostrzeżenia został później zaktualizowany do /warnon:3395.

Opcjonalne ostrzeżenia dotyczące niejawnych konwersji

Konwersje ukierunkowane na typy i niejawne mogą źle współdziałać z wnioskowaniem typu i prowadzić do kodu, który jest trudniejszy do zrozumienia. Z tego powodu istnieją pewne środki zaradcze, aby zapewnić, że ta funkcja nie jest nadużywana w kodzie języka F#. Po pierwsze, zarówno typ źródłowy, jak i docelowy musi być silnie znany, bez niejednoznaczności lub dodatkowego wnioskowania typu. Po drugie, można aktywować ostrzeżenia umożliwiające zgłaszanie dowolnego użycia niejawnych konwersji z jednym ostrzeżeniem domyślnie:

  • /warnon:3388 (dodatkowa niejawna emisja upcast)
  • /warnon:3389 (niejawne rozszerzanie liczbowe)
  • /warnon:3391 (op_Implicit w argumentach innych niż metoda, domyślnie)
  • /warnon:3395 (op_Implicit argumentów metody)

Jeśli twój zespół chce zablokować wszystkie zastosowania niejawnych konwersji, możesz również określić /warnaserror:3388wartości , /warnaserror:3389, /warnaserror:3391i /warnaserror:3395.

Formatowanie liczb binarnych

Język F# 6 dodaje %B wzorzec do dostępnych specyfikatorów formatu dla formatów liczb binarnych. Rozważ następujący kod języka F#:

printf "%o" 123
printf "%B" 123

Ten kod wyświetla następujące dane wyjściowe:

173
1111011

Ta funkcja implementuje F# RFC FS-1100.

Odrzuca powiązania użycia

Język F# 6 umożliwia _ użycie w powiązaniu use , na przykład:

let doSomething () =
    use _ = System.IO.File.OpenText("input.txt")
    printfn "reading the file"

Ta funkcja implementuje F# RFC FS-1102.

WbudowanyifLambda

Kompilator języka F# zawiera optymalizator, który wykonuje tworzenie podkreślinia kodu. W języku F# 6 dodaliśmy nową funkcję deklaratywną, która pozwala kodowi opcjonalnie wskazać, że jeśli argument jest określany jako funkcja lambda, ten argument powinien być zawsze podsycony w lokacjach wywołań.

Rozważmy na przykład następującą iterateTwice funkcję, aby przejść przez tablicę:

let inline iterateTwice ([<InlineIfLambda>] action) (array: 'T[]) =
    for j = 0 to array.Length-1 do
        action array[j]
    for j = 0 to array.Length-1 do
        action array[j]

Jeśli lokacja połączenia to:

let arr = [| 1.. 100 |]
let mutable sum = 0
arr  |> iterateTwice (fun x ->
    sum <- sum + x)

Następnie po podkreśliniu i innych optymalizacjach kod staje się:

let arr = [| 1.. 100 |]
let mutable sum = 0
for j = 0 to arr.Length-1 do
    sum <- sum + arr[j]
for j = 0 to arr.Length-1 do
    sum <- sum + arr[j]

W przeciwieństwie do poprzednich wersji języka F#, ta optymalizacja jest stosowana niezależnie od rozmiaru wyrażenia lambda. Ta funkcja może również służyć do implementowania wyrejestrowywania pętli i bardziej niezawodnych przekształceń podobnych.

Ostrzeżenie o wyrażeniu zgody (/warnon:3517domyślnie wyłączone) może być włączone, aby wskazać miejsca w kodzie, w których InlineIfLambda argumenty nie są powiązane z wyrażeniami lambda w lokacjach wywołań. W normalnych sytuacjach to ostrzeżenie nie powinno być włączone. Jednak w niektórych rodzajach programowania o wysokiej wydajności może być przydatne zapewnienie, że cały kod jest wbudowany i spłaszczone.

Ta funkcja implementuje F# RFC FS-1098.

Kod możliwy do wznowienia

Obsługa task {…} języka F# 6 jest oparta na podstawach o nazwie kodwznawialny RFC FS-1087. Kod możliwy do wznowienia to funkcja techniczna, która może służyć do tworzenia wielu rodzajów asynchronicznych maszyn o wysokiej wydajności i uzyskiwania maszyn stanu.

Dodatkowe funkcje kolekcji

Program FSharp.Core 6.0.0 dodaje pięć nowych operacji do podstawowych funkcji kolekcji. Są to następujące funkcje:

  • List/Array/Seq.insertAt
  • List/Array/Seq.removeAt
  • List/Array/Seq.updateAt
  • List/Array/Seq.insertManyAt
  • List/Array/Seq.removeManyAt

Wszystkie te funkcje wykonują operacje kopiowania i aktualizacji odpowiedniego typu kolekcji lub sekwencji. Ten typ operacji jest formą "aktualizacji funkcjonalnej". Przykłady użycia tych funkcji można znaleźć w odpowiedniej dokumentacji, na przykład List.insertAt.

Rozważmy na przykład model, komunikat i logikę aktualizacji dla prostej aplikacji "Lista zadań do wykonania" napisanych w stylu Elmish. W tym miejscu użytkownik wchodzi w interakcję z aplikacją, generuje komunikaty, a update funkcja przetwarza te komunikaty, tworząc nowy model:

type Model =
    { ToDo: string list }

type Message =
    | InsertToDo of index: int * what: string
    | RemoveToDo of index: int
    | LoadedToDos of index: int * what: string list

let update (model: Model) (message: Message) =
    match message with
    | InsertToDo (index, what) ->
        { model with ToDo = model.ToDo |> List.insertAt index what }
    | RemoveToDo index ->
        { model with ToDo = model.ToDo |> List.removeAt index }
    | LoadedToDos (index, what) ->
        { model with ToDo = model.ToDo |> List.insertManyAt index what }

Dzięki tym nowym funkcjom logika jest jasna i prosta i opiera się tylko na niezmiennych danych.

Ta funkcja implementuje F# RFC FS-1113.

Mapa zawiera klucze i wartości

W programie Map FSharp.Core 6.0.0 typ obsługuje teraz właściwości Klucze i wartości . Te właściwości nie kopiują podstawowej kolekcji.

Ta funkcja jest udokumentowana w F# RFC FS-1113.

Dodatkowe funkcje wewnętrzne dla biblioteki NativePtr

Program FSharp.Core 6.0.0 dodaje nowe funkcje wewnętrzne do modułu NativePtr :

  • NativePtr.nullPtr
  • NativePtr.isNullPtr
  • NativePtr.initBlock
  • NativePtr.clear
  • NativePtr.copy
  • NativePtr.copyBlock
  • NativePtr.ofILSigPtr
  • NativePtr.toILSigPtr

Podobnie jak w przypadku innych funkcji w programie NativePtr, te funkcje są wciśnięty, a ich użycie emituje ostrzeżenia, chyba że /nowarn:9 jest używany. Korzystanie z tych funkcji jest ograniczone do typów niezarządzanych.

Ta funkcja jest udokumentowana w F# RFC FS-1109.

Dodatkowe typy liczbowe z adnotacjami jednostkowymi

W języku F# 6 następujące typy lub aliasy skrótów typów obsługują teraz adnotacje unit-of-measure. Nowe dodatki są wyświetlane pogrubioną czcionką:

Alias języka F# Typ CLR
float32/single System.Single
float/double System.Double
decimal System.Decimal
sbyte/int8 System.SByte
int16 System.Int16
int/int32 System.Int32
int64 System.Int64
byte/uint8 System.Byte
uint16 System.UInt16
uint/uint32 System.UInt32
uint64 System.UIn64
nativeint System.IntPtr
unativeint System.UIntPtr

Można na przykład dodać adnotację do niepodpisanej liczby całkowitej w następujący sposób:

[<Measure>]
type days

let better_age = 3u<days>

Ta funkcja jest udokumentowana w F# RFC FS-1091.

Ostrzeżenia informacyjne dla rzadko używanych operatorów symbolicznych

Język F# 6 dodaje miękkie wskazówki, które nie normalizują użycia :=elementów , !, incri decr w języku F# 6 i nowszych. Użycie tych operatorów i funkcji powoduje wygenerowanie komunikatów informacyjnych z prośbą o zastąpienie kodu jawnym użyciem Value właściwości .

W programowaniu języka F# komórki referencyjne mogą być używane dla rejestrów modyfikowalnych przydzielonych stertami. Chociaż są one od czasu do czasu przydatne, rzadko są potrzebne w nowoczesnym kodowaniu języka F#, ponieważ let mutable można ich użyć zamiast tego. Podstawowa biblioteka języka F# zawiera dwa operatory := i ! dwie funkcje incr , a decr w szczególności powiązane z wywołaniami referencyjnymi. Obecność tych operatorów sprawia, że komórki referencyjne są bardziej centralne dla programowania F#, niż muszą, wymagając od wszystkich programistów języka F# znajomości tych operatorów. ! Ponadto operator może być łatwo pomylony z operacją not w języku C# i innych językach, potencjalnie subtelnym źródłem usterek podczas tłumaczenia kodu.

Uzasadnieniem tej zmiany jest zmniejszenie liczby operatorów, które musi wiedzieć programista języka F#, a tym samym uproszczenie języka F# dla początkujących.

Rozważmy na przykład następujący kod języka F# 5:

let r = ref 0

let doSomething() =
    printfn "doing something"
    r := !r + 1

Po pierwsze komórki referencyjne są rzadko potrzebne we współczesnym kodowaniu języka F#, co let mutable zwykle może być używane:

let mutable r = 0

let doSomething() =
    printfn "doing something"
    r <- r + 1

Jeśli używasz komórek referencyjnych, język F# 6 emituje ostrzeżenie informacyjne z prośbą o zmianę ostatniego wiersza na r.Value <- r.Value + 1, i połączenie cię z dalszymi wskazówkami dotyczącymi odpowiedniego użycia komórek referencyjnych.

let r = ref 0

let doSomething() =
    printfn "doing something"
    r.Value <- r.Value + 1

Te komunikaty nie są ostrzeżeniami; są to "komunikaty informacyjne" wyświetlane w środowisku IDE i danych wyjściowych kompilatora. Język F# pozostaje zgodny z poprzednimi wersjami.

Ta funkcja implementuje F# RFC FS-1111.

Narzędzia języka F#: platforma .NET 6 domyślna do obsługi skryptów w programie Visual Studio

Jeśli otworzysz lub wykonasz skrypt języka F# () w.fsx programie Visual Studio, domyślnie skrypt zostanie przeanalizowany i wykonany przy użyciu platformy .NET 6 z wykonywaniem 64-bitowym. Ta funkcja była dostępna w wersji zapoznawczej w nowszych wersjach programu Visual Studio 2019 i jest teraz domyślnie włączona.

Aby włączyć wykonywanie skryptów programu .NET Framework, wybierz pozycję Narzędzia Opcje>narzędzia F# Tools>>F# Interactive. Ustaw opcję Użyj skryptów platformy .NET Core na wartość false, a następnie uruchom ponownie okno interaktywne języka F#. To ustawienie ma wpływ zarówno na edytowanie skryptu, jak i wykonywanie skryptu. Aby włączyć wykonywanie 32-bitowe dla skryptów programu .NET Framework, ustaw również 64-bitową wartość F# Interactive na false. Nie ma opcji 32-bitowej dla skryptów platformy .NET Core.

Narzędzia języka F#: przypinanie wersji zestawu SDK skryptów języka F#

Jeśli skrypt zostanie wykonany przy użyciu w dotnet fsi katalogu zawierającym plik global.json z ustawieniem zestawu SDK platformy .NET, do wykonania i edytowania skryptu zostanie użyta wymieniona wersja zestawu .NET SDK. Ta funkcja jest dostępna w nowszych wersjach języka F# 5.

Załóżmy na przykład, że w katalogu znajduje się skrypt z następującym plikiem global.json określającym zasady wersji zestawu SDK platformy .NET:

{
  "sdk": {
    "version": "5.0.200",
    "rollForward": "minor"
  }
}

Jeśli skrypt zostanie wykonany przy użyciu polecenia dotnet fsi, z tego katalogu będzie uwzględniana wersja zestawu SDK. Jest to zaawansowana funkcja, która umożliwia "zablokowanie" zestawu SDK używanego do kompilowania, analizowania i wykonywania skryptów.

Jeśli otworzysz i edytujesz skrypt w programie Visual Studio i innych środowiskach IDE, narzędzie będzie przestrzegać tego ustawienia podczas analizowania i sprawdzania skryptu. Jeśli zestaw SDK nie zostanie znaleziony, musisz go zainstalować na maszynie dewelopera.

W systemach Linux i innych systemach Unix można połączyć je z shebang, aby również określić wersję językową do bezpośredniego wykonywania skryptu. Prosty shebang dla script.fsx to:

#!/usr/bin/env -S dotnet fsi

printfn "Hello, world"

Teraz skrypt można wykonać bezpośrednio za pomocą polecenia script.fsx. Można to połączyć z określoną, inną niż domyślną wersją języka w następujący sposób:

#!/usr/bin/env -S dotnet fsi --langversion:5.0

Uwaga

To ustawienie jest ignorowane przez narzędzia do edycji, które przeanalizują skrypt przy założeniu najnowszej wersji językowej.

Usuwanie starszych funkcji

Od czasu języka F# 2.0 niektóre przestarzałe funkcje od dawna otrzymują ostrzeżenia. Użycie tych funkcji w języku F# 6 powoduje błędy, chyba że jawnie użyjesz polecenia /langversion:5.0. Funkcje, które dają błędy, to:

  • Wiele parametrów ogólnych przy użyciu nazwy typu postfiksu, na przykład (int, int) Dictionary. Staje się to błędem w języku F# 6. Zamiast tego należy użyć standardowej składni Dictionary<int,int> .
  • #indent "off". Staje się to błędem.
  • x.(expr). Staje się to błędem.
  • module M = struct … end . Staje się to błędem.
  • Użycie danych wejściowych *.ml i *.mli. Staje się to błędem.
  • Użycie polecenia (*IF-CAML*) lub (*IF-OCAML*). Staje się to błędem.
  • Używanie operatorów land, , lor, lxorlsl, lsrlub asr jako operatorów przyrostków. Są to słowa kluczowe w języku F#, ponieważ były słowami kluczowymi w pliku OCaml i nie są zdefiniowane w języku FSharp.Core. Użycie tych słów kluczowych spowoduje teraz emitować ostrzeżenie.

Implementuje to F# RFC FS-1114.