Novinky v F# 11

Vítá vás F# 11! Tato verze vám pomůže zachovat jedinečné doménové modely, opakovaně používat obecné algoritmy, koordinovat asynchronní práci a snižovat využití paměti kompilátoru.

Děkujeme komunitě F# za návrhy, implementace, kontroly a testy, které stojí za těmito změnami. Odkazy na přispěvatele doprovázejí níže uvedené funkce.

Note

Příklady vyžadují kompilátor F# 11 a FSharp.Core 11 s podporou uvedených funkcí. V sadě SDK verze .NET 11.0.100 vyžadují <LangVersion>preview</LangVersion>funkce F# 11.2 ; viz nastavení jazyka a plánovaná podpora sady SDK. Příklad runtime-async má další požadavky na modul runtime; samotná starší sada .NET 11 SDK nemusí obsahovat všechny změny.

Rozložení záznamů: jedinečné modely bez opakovaných mapování

Aby modely pro úložiště a aplikaci zůstaly oddělené, rozprostření záznamů znovu využívá jejich sdílená pole bez opakovaných deklarací. Zde modely sdílejí údaje o zákaznících, ale používají různé typy identifikátorů:

open System

type CustomerId = CustomerId of Guid

type CustomerDetails = { Name: string; PreferredLanguage: string }

type CustomerRow =
    { ...CustomerDetails
      Id: string
      InternalNote: string }

type Customer =
    { ...CustomerDetails
      Id: CustomerId }

...CustomerDetails přispívá deklaracemi polí. Na hranici se hodnoty zkopírují pomocí ...row, zatímco převod analyzuje identifikátor:

let toCustomer (row: CustomerRow) =
    match Guid.TryParse row.Id with
    | true, id -> Ok ({ ...row; Id = CustomerId id }: Customer)
    | false, _ -> Error "Invalid customer identifier."

Cíl zachová sdílené údaje, nahradí Id a vyloučí InternalNote. Guid.TryParse, nikoli spread operátor, validuje identifikátor; neplatný vstup způsobí Error.

Řeší se tím problém mapování na aplikační vrstvě. Rozpětí hodnot také kombinují pojmenované a anonymní záznamy; Rozložení typu vytváří pouze pojmenované záznamy. Pozdější pole mají přednost a zdroje se spustí jednou v pořadí. Příklady složení a rizika sdíleného vývoje najdete v tématu Operátory rozložení záznamů .

Díky @brianrourkeboll za rekordní rozpětí.

Členy rozšíření pro obecné algoritmy, operátory a uspořádané n-tice

Pokud chcete sdílet algoritmus mezi existujícími typy, F# 11.2 umožňuje, aby členy rozšíření splňovaly omezení staticky řešeného parametru typu (SRTP). Jedna transformace může například zpracovávat seznamy a volby bez obalových typů.

Note

V .NET 11.0.100 SDK povolte <LangVersion>preview</LangVersion>, kde se rozšíření vyřeší, a otevřete modul rozšíření. Viz požadavky na knihovnu.

Sdílet transformaci mezi seznamy a možnostmi

S map knihovny v dosahu může aplikace vyčistit kteroukoli z obou reprezentací, změnit typy elementů a vytvářet transformace:

let inline cleanNames values =
    map (fun (name: string) -> name.Trim().ToUpperInvariant()) values

let batch: string list = cleanNames [ " Ada "; " grace " ]
let optional: string option = cleanNames (Some " linus ")
let missing: string option = cleanNames (None: string option)
let lengths: int list = map String.length batch
let nested: int option list = map (map String.length) [ Some "abc"; None ]

printfn "%A; %A; %A" batch optional missing
printfn "%A; %A" lengths nested
// ["ADA"; "GRACE"]; Some "LINUS"; None
// [3; 5]; [Some 3; None]

Adaptéry v Structural.Adapters používají známé operace, místo vnucování nových kontejnerů:

type List<'T> with
    static member Map(source: 'T list, mapping: 'T -> 'U) : 'U list =
        List.map mapping source

type Option<'T> with
    static member Map(source: 'T option, mapping: 'T -> 'U) : 'U option =
        Option.map mapping source

Sdílená funkce v Structural.Algorithms požádá vstupní typ o statický člen Map:

let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
    (^Input :
        (static member Map: ^Input * ('T -> 'U) -> ^Output)
        (source, mapping))

^Input a ^Output jsou staticky vyřešené typy. Při každém konkrétním volání kompilátor vybere člena s požadovaným podpisem. Rozšíření nepřidávají implementaci rozhraní. Kompletní aplikace otevře oba moduly.

Operátory, kontext a výstupní typy

Stejný mechanismus podporuje operátory n-tic a šíření kontextu a konstrukci řízenou výstupem. Nativní záhlaví n-tic vyžadují povolení funkce F# 11.2 ve svých deklaracích a také v místech volání při řešení rozšíření.

Samostatný doplněk, AllowOverloadOnReturnTypeumožňuje jedné továrně deklarovat přetížení rozlišující jejich návraty:

type Inject =
    [<AllowOverloadOnReturnType>]
    static member Pure(value: 'T) : 'T list = [value]

    [<AllowOverloadOnReturnType>]
    static member Pure(value: 'T) : 'T option = Some value

let batch: string list = Inject.Pure "ready"
let optional: string option = Inject.Pure "ready"
// ["ready"] and Some "ready"

Tento atribut Core 11 nevyžaduje náhled. Anotace výsledku vybere přetížení; bez ní zůstane volání nejednoznačné. Viz Výběr podle návratového typu.

Díky @gusty za rozšíření podpory obecného programování.

integrace .NET a zkušenosti při vývoji

Přímí delegáti zachovávají skutečné rozhraní obslužné rutiny.

Aby bylo možné frameworkům zpřístupnit názvy obslužných funkcí a atributy vazeb, může F# 11 místo obálky cílit přímo na kompatibilní pojmenovanou funkci. ASP.NET Core minimálních rozhraní API pak můžou číst trasu, službu a kontrakt hlaviček:

open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection

type ShipmentStore() =
    member _.Status trackingId =
        if trackingId = "SHP-1042" then "Dispatched" else "Unknown"

let status
    (trackingId: string)
    ([<FromServices>] store: ShipmentStore)
    ([<FromHeader(Name = "X-Customer")>] customer: string) =
    $"{customer}: {trackingId} is {store.Status trackingId}"

let handler = Func<string, ShipmentStore, string, string>(status)

Vizte spustitelný projekt a pravidla pro konstrukci/kompatibilitu. Zachytávání a přizpůsobení argumentů může stále vyžadovat obálky.

Děkujeme @kerams za přímé delegace.

Opětovné použití dokumentace XML

Chcete-li sdílené komentáře k API udržovat na jednom místě, umístěte je do shared.xml:

<docs>
  <tracking>
    <remarks>An unrecognized tracking identifier has status Unknown.</remarks>
  </tracking>
</docs>

Když je povoleno GenerateDocumentationFile, F# 11 rozvine <include> do vygenerovaných položek členů:

/// <summary>Find the latest status of a shipment.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let status trackingId =
    if trackingId = "SHP-1042" then "Dispatched" else "Unknown"

/// <summary>Find the latest statuses of several shipments.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let statuses trackingIds =
    trackingIds |> List.map (fun id -> id, status id)

Implementace include aktualizuje oba popisy z jednoho fragmentu. Resolver nástrojů samostatně zpřístupňuje základní popisy pro <inheritdoc/>; kompilátor tuto značku ponechává pro konzumenty dokumentace. Viz dokumentaci XML, kde najdete příklady a pravidla určování.

Děkujeme @T-Gro za sdílenou dokumentaci XML v FSharp.Core.

Ladění inline funkcí v neoptimalizovaných sestaveních

Chcete-li prozkoumat argumenty vložené pomocné funkce, nastavte zarážku na let total v neoptimalizovaném sestavení a přejděte krokováním do addFee. F# 11 teď dává přednost volatelným metodám v těchto buildech:

let inline addFee (fee: decimal) (amount: decimal) =
    let total = amount + fee
    total

let invoiceTotal = addFee 2m 40m
printfn "%M" invoiceTotal
// 42

Optimalizovaná sestavení zachovávají inline expanzi. Některé konstrukty se stále musí rozvinout. Viz část Ladění vložených funkcí, kde najdete informace o těchto omezeních a ovládacím prvku --always-inline.

Asynchronnost a koordinace úkolů

Pro připojení pracovních postupů jazyka F# pomocí rozhraní API založených na úlohách přidává FSharp.Core 11 propojení podporující zrušení, omezené dávky a transformace výsledků.

Omezit počet nevyřízených vstupně-výstupních operací, nejen kolekci výsledků

Velká dávka čtení může zahájit více operací, než by aplikace měla mít současně rozpracovaných. K rozhodnutí o tom, kdy práce založená na úlohách začne, potřebuje koordinátor factory, nikoli již spuštěné úlohy:

open System.IO
open System.Threading

let readFiles (paths: seq<string>) (ct: CancellationToken) =
    paths
    |> Seq.map (fun path ->
        fun token -> File.ReadAllTextAsync(path, token))
    |> Task.parallelLimit 4 ct

Nanejvýš čtyři operace čtení probíhají současně; výsledky zachovávají pořadí vstupu. Selhání vyžaduje zrušení souvisejících požadavků prostřednictvím zadaných tokenů. To omezuje souběžnost, nikoli počet požadavků za sekundu. Viz funkce Async a task pro sekvenční varianty a varianty s výsledkem typu unit.

Zachovejte zrušení a původní výjimku přes hranici

Když pracovní postup spustí externí práci, měl by jeho token cestovat s touto externí prací. Async.StartTaskImmediate předá token spuštěného pracovního postupu do objektu pro vytváření úloh a čeká na dokončení:

open System.IO

let tryReadFile path =
    async {
        try
            let! contents =
                Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
            return Some contents
        with :? FileNotFoundException ->
            return None
    }

Selhání jediného úkolu je zpracováno touto obslužnou rutinou jako základní FileNotFoundException. Pro již vytvořený úkol nebo ValueTask použijte Async.Await; starší Async.AwaitTask zachovává chování spočívající v zapouzdření výjimek.

Pro volající používající úlohy Task.startAsyncImmediate token workflow poskytuje reverse bridge. Spustí se na volajícím vlákně do prvního asynchronního pozastavení.

Používejte odpovídající pomocné funkce pro krátké transformace

Chcete-li transformovat hodnotu typu Result bez úplného výpočetního výrazu, použijte odpovídající pomocné rutiny v Microsoft.FSharp.Control. Například načtěte soubor, zjistěte jeho délku a vraťte běžné selhání jako Error:

open System.IO
open System.Threading

let fileLength (path: string) (ct: CancellationToken) =
    File.ReadAllTextAsync(path, ct)
    |> Task.map String.length
    |> Task.catch

Soubor obsahující Report 1 vrátí Ok 8; chybějící soubor poskytuje Error obsahující FileNotFoundException. Zrušení se stále projeví. Async a ValueTask nabízejí odpovídající mapfunkce, binda pomocné funkce pro chyby; viz asynchronní funkce a funkce úloh. Pro větve nebo několik závislých hodnot použijte výpočetní výraz.

Pomocné rutiny ValueTask vyžadují sestavení netstandard2.1 Core nebo net10.0 a jednorázové použití.

Děkujeme @bartelink za příspěvky k rozhraní API pro async/task.

Opětovné vyvolání po asynchronním hlášení selhání

Provozní protokolování by mělo přidávat informace, aniž by měnilo to, co volající zachytí, nebo skrývalo, kde selhání začalo. Obslužné rutiny výpočetních výrazů teď mohou použít reraise () i poté, co čekají na protokolovací modul:

Note

Rethrow ve výpočetním výrazu je funkcionalita F# 11.2. V .NET 11.0.100 SDK povolte <LangVersion>preview</LangVersion>.

open System
open System.IO
open System.Threading.Tasks

let readReport (logFailure: exn -> Task) path =
    task {
        try
            return! File.ReadAllTextAsync(path)
        with :? IOException as error ->
            do! logFailure error
            return reraise ()
    }

Po úspěšném zaznamenání do protokolu se dál šíří stejná výjimka i původní informace o místě jejího vyvolání. Při selhání protokolovacího nástroje může být původní záznam nahrazen. To řeší požadavek na asynchronní pozorovatelnost bez runtime asynchronnosti. Viz Opětovné vyvolání výjimky z výpočetního výrazu.

Děkujeme @xperiandri za tuto podporu.

FSharp.Core může vycházet z moderního .NET

Pokud chcete používat moderní rozhraní API bez vyřazení starších cílů, přidává net10.0 funkce FSharp.Core vedle netstandard2.0 sebe a netstandard2.1. NuGet vybere kompatibilní sestavení. Aktualizované balení nástrojů také umožňuje asynchronní uvolňování prostředků založené na úlohách v F# Interactive, stejně jako v kompilovaných projektech. Viz cílové architektury FSharp.Core.

@T-Gro Děkujeme za cílení na moderní Core a balení nástrojů SDK.

Runtime async pro autory sestavení

Autoři tvůrce můžou nechat modul runtime pozastavit a obnovit označené metody místo použití stavových počítačů generovaných kompilátorem. Experimentální builder definovaný knihovnou zpřístupňuje mechanismus jako výpočetní výraz:

let readManifest (path: string) =
    runtimeTask {
        let! text = System.IO.File.ReadAllTextAsync path
        return text.Length
    }

Note

Tento experimentální tvůrce používá funkci F# 11.2, kde je definována nebo rozšířena její vložená implementace; v sadě .NET 11.0.100 SDK povolte verzi Preview. Vyžaduje také sestavení a reference Core 11 net10.0 a modul runtime s podporou runtime async. Tady je ověřená kombinace runtime/reference .NET 11 RC1. Cílení na .NET 10 není dostatečné. Viz asynchronní experimenty runtime.

FSharp.Core tuto ukázku nedoručí jako tvůrce pro obecné účely. Běžný kód task a async se na něj automaticky nepřepíná a volající již zkompilovaného rozhraní API vracejícího úlohu preview ze své podstaty nepotřebují.

Díky @majocha za asynchronní runtime.

Nativní text vhodný pro AOT a dvojrozměrná pole

Aby bylo možné podporovat nativní AOT, F# 11 odebere z běžnějších operací s textem a poli požadavky na formátování založené na reflexi a dynamické přidělování.

Pokud už máte řetězec, k jeho zápisu nepotřebujete jen značku "%s". Nové funkce Core print a printn přijímají hodnoty pomocí převodu string jazyka F#; printn přidá znak nového řádku:

let report (processed: int) (total: decimal) (elapsed: TimeSpan) : string =
    $"{processed} items, total {total:F2}, elapsed {elapsed.TotalMilliseconds:F0} ms"

printn (report 12 145.50m (TimeSpan.FromMilliseconds 250.0))
// 12 items, total 145.50, elapsed 250 ms

Interpolace řetězců používá přímý převod a String.Concat ve více případech. Zástupné symboly formátu .NET, jako je {total:F2}, používají invariantní formátování a provádějí boxing hodnoty; %A a formáty printf s určenou přesností mohou i nadále potřebovat formátování printf. Vyberte explicitní národní prostředí pro lokalizovaný výstup.

S --reflectionfree záznamy a sjednocené typy také získají vygenerovaný ToString výstup. Příklady kultur a podporované cesty najdete v tématu Interpolace a nativní AOT.

Děkujeme @bbatsov za funkce pro tisk a @charlesroddie za implementaci interpolace.

Zachovat běžné operace mřížky s indexováním od nuly

Mřížka založená na nule by neměla vyžadovat náhradní modul Array2D jen proto, aby se zabránilo cestě alokace dynamického kódu. Aktualizované základní implementace podporují známou inicializaci a transformaci:

let grid = Array2D.init 3 4 (fun row column -> row * 4 + column)
let doubled = grid |> Array2D.map (fun value -> value * 2)
let snapshot = Array2D.copy doubled

printn $"{snapshot.GetLength(0)} x {snapshot.GetLength(1)}; last = {snapshot[2, 3]}"
// 3 x 4; last = 22

Array2D.create, rebase a init používají cestu přidělení založenou na nule napříč sestaveními Core. Implementace zachovávající hranice map, mapi a copy mohou v sestavení net10.0 používat novější rozhraní API pro přidělování .NET; sestavení .NET Standard si zachovávají záložní řešení. Pole s indexováním od nenuly nejsou zastaralá, ale jejich nativní omezení přidělení zůstávají.

Informace o cestách přidělení a zbývajících omezeních najdete v článku Dvourozměrná pole a Native AOT. Zkontrolujte diagnostiku publikování celé aplikace.

Díky @T-Gro za vylepšení Array2D.

Menší využití paměti a méně opakované práce

F# 11 snižuje opakovanou analýzu kompilátoru a některé alokace v aplikaci. Tyto úlohy mají vliv na různé úlohy.

Sdílení odkazovaných knihoven napříč projekty

Aby nedocházelo k opakovaným kopiím sdílených binárních závislostí, může služba jazyka sdílet vhodné importované grafy knihoven napříč projekty.

Při retained-memory experimentu implementačního PR kleslo řešení ReSharper.FSharp o deseti projektech z 1 547,2 MB na 1 002,2 MB s povoleným sdílením. To je měření typu zapnuto/vypnuto pro danou úlohu, nikoli obecné snížení F# z 10 na 11. Řešení, v nichž převažují odkazy na projekty spíše než sdílené binární odkazy, můžou zaznamenat mnohem menší zlepšení.

Čtení metadat a vložená specializace také snižují dočasné přidělení, samostatnou metriku. Viz Výkon nástrojů pro rozsah a ovládání.

Díky @auduchinok pro importy sdílených knihoven.

Kompilovat nezávislé úlohy paralelně ve výchozím nastavení

Paralelní kompilace je nyní ve výchozím nastavení povolena, přičemž je zachován deterministický výstup i pravidla pořadí zdrojových souborů. Přetížení ukládání do mezipaměti a vylepšení strážených vzorů zabraňuje dalším opakovaným pracím. Visual Studio/FCS také snižují analýzu nezměněných dokumentů a omezují souběžné kontroly; viz Výkon nástrojů.

Vyhněte se callbacku a režii přidělení prázdných výsledků

Vybrané List funkce nyní mají Array vložená zpětná volání. Pro své vlastní pomocné funkce OptimizeClosureIfNotInlined vkládané přímo do kódu jednorázově adaptuje neprůhledný curryovaný callback:

let inline foldItems
    ([<InlineIfLambda; OptimizeClosureIfNotInlined>] folder)
    state
    (items: 'T[]) =
    let mutable total = state
    for item in items do
        total <- folder total item
    total

Ostatní přidělení můžou zůstat. Požadavky najdete v Optimalizace částečně aplikovaných zpětných volání.

Prázdné výsledky jsou dalším běžným případem úspěšně odvedené práce. Motivační pracovní zátěž FSharpLintu opakovaně kombinovala pravidla, která nevygenerovala žádná varování. Jádro teď znovu používá sdílená prázdná pole v dalších operacích:

let checkTitle (title: string) =
    if String.IsNullOrWhiteSpace title then [|"A title is required."|]
    else Array.empty

let warnings = ["Introduction"; "Installation"] |> List.toArray |> Array.collect checkTitle
let sharedEmpty = obj.ReferenceEquals(warnings, Array.empty<string>)
// true: no new empty result array

Tato změna odebere redundantní objekty prázdného pole, ne každou alokaci v kanálu. Nespoléhejte na to, že samostatné prázdné výsledky mají odlišné referenční identity.

Díky zpětnému @T-Grovolání vložené kolekce a sdíleným prázdným polím.

Přesnější volání rozhraní API a každodenní usnadnění

Vyžadovat názvy, když se může změnit pořadí generovaných parametrů

Aby autor chránil vygenerovaná rozhraní API před tichými změnami pořadí parametrů, může vyžadovat pojmenované argumenty:

Note

Vynucení je funkce F# 11.2; v sadě .NET 11.0.100 SDK povolte verzi Preview. Vyžaduje se také dostupná System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute definice. Kompletní ukázka poskytuje definici kompatibility pro reference, kterým chybí definice kompatibility.

open System.Diagnostics.CodeAnalysis

type Query =
    [<RequireNamedArguments>]
    static member Execute(numerator: decimal, denominator: decimal) =
        numerator / denominator

let ratio = Query.Execute(numerator = 3m, denominator = 4m)
// 0.75M; Query.Execute(3m, 4m) is rejected.

Přeuspořádání deklarace zachovává výsledek pojmenovaného volání, 0.75M; poziční volání je odmítnuto. Pro vygenerovaný případ použití rozhraní API musí poskytovatel emitovat anotaci.

Rozpoznat již zabalený obecný argument

Chcete-li zabránit zabalení existujícího úkolu jako hodnoty prostého výsledku, nové záchranné pravidlo přetížení dává přednost Task<'T> před holým 'T:

Note

Nejkonkrétnější pravidlo pro rozhodnutí shody a chování priority přetížení jsou funkce F# 11.2; v sadě SDK .NET 11.0.100 povolte verzi Preview.

open System.Threading.Tasks

let pending = Task.FromResult 42
let wrapped = ValueTask<_>(pending)
let answer: int = wrapped.Result
// ValueTask<int>, not ValueTask<Task<int>>

Výsledek je ValueTask<int>, ne ValueTask<Task<int>>. Limity pravidla najdete v části Výběr přetížení.

Preferujte nové přetížení bez odebrání starého entry pointu.

Knihovna může zachovat starou diagnostickou metodu pro existující binární soubory při přidávání přetížení, které zahrnuje zdrojové umístění volajícího. OverloadResolutionPriorityAttribute dává přednost nově zkompilovaným volajícím:

open System.Runtime.CompilerServices

type Check =
    static member That(condition: bool) =
        if condition then "OK" else "Check failed."

    [<OverloadResolutionPriority(1)>]
    static member That(condition: bool, [<CallerLineNumber>] ?line: int) =
        if condition then "OK" else $"Check failed at line {defaultArg line 0}."

let stock = 0
let diagnostic = Check.That(stock > 0)
// The diagnostic includes the caller's source line.

Nezměněné volání vybere přetížení zohledňující řádky. Viz Výběr přetížení metod pro pravidla rozsahu a kompatibility.

Použití podmíněných záruk nulovatelnosti v platformě .NET

Pokud je vaše cesta k souboru nenulová, neměl by výsledek rozhraní Path API s možnou hodnotou null vynutit kontrolu, kterou jeho vlastní kontrakt vylučuje. F# 11 využívá existující .NET NotNullIfNotNull příslib: nenulový pojmenovaný vstup znamená nenulový výsledek.

open System.IO

let fileName: string = Path.GetFileName("reports/annual.csv")
let extension: string = Path.GetExtension(fileName)
printn (extension.ToUpperInvariant())
// .CSV

let fileNameOf (path: string | null) : string | null =
    Path.GetFileName path

Povolte <Nullable>enable</Nullable>. Vstupy s možnou hodnotou null stále potřebují ošetření hodnot null; viz podmíněné kontrakty návratové hodnoty. Pro neomezená rozhraní API jazyka C# T? použijte samostatnou Unchecked.withNull pomocnou rutinu.

Díky @kerams kvůli podpoře podmíněné nulové hodnoty.

Menší vylepšení jazyka

Chcete-li volat typ záznamu pomocí syntaxe konstruktoru .NET, použijte v jazyce F# 11.2 konstruktory záznamů:

type Person = { Name: string; Age: int }
let person = Person(Name = "Ada", Age = 36)

Pojmenované argumenty se vyhýbají závislosti na pořadí parametrů. Díky @charlesroddie za tuto funkci.

U podmíněného zdroje #elif se vyhnete vnořeným skupinám #if :

#if EU_WAREHOUSE
let dispatchTimeZone = "Europe/Paris"
#elif US_WAREHOUSE
let dispatchTimeZone = "America/New_York"
#else
let dispatchTimeZone = "UTC"
#endif

System.Console.WriteLine(dispatchTimeZone)

První odpovídající větev vyhraje. Ukázkový projekt definuje EU_WAREHOUSE a vytiskne Europe/Paris; pokud není definován ani jeden symbol, výstup je UTC.

Související nástroje a scénáře interoperability:

Před upgradem

Při aktualizaci mějte na paměti tyto významné změny:

Ovlivněný kód Action
Knihovny se .fsi soubory Kopírovat vynucené atributy kontraktů do podpisů; vynechání jsou nyní chybami FS3888.
Vložené knihovny založené na rozšířeních Zkontrolujte odvozené podpisy a požadavky producenta/příjemce. Odvození F# 11.2 může zachovat obecnější omezení.
Delegáti metod instance Ověřte příjemce před přímou konstrukcí, protože při příjemci s hodnotou null nyní dochází k selhání.
Lokalizovaný interpolovaný výstup Formátovací položky ve formátu .NET používají invariantní jazykovou verzi. Zvolte explicitní jazykovou a místní verzi, pokud je určen lokalizovaný výstup.
Vlastní tvůrci s finální do! Dodání vazby a dokončení jednotek; final do! už nevybírej konečné členy přeposílání.
Nedokončené let! Přidejte následující tělo výpočtu; neúplná vazba teď má přímočařejší diagnostické hlášení.
Předchozí jazyková nastavení Podporované číslované režimy začínají na F# 8; režim nestriktního odsazení je odstraněn. Viz Jazyková verze a odsazení.
Bitové operace s enum Používejte výčty s celočíselným podkladovým typem; neplatné podkladové typy jsou odmítnuty.