Az F# 11 újdonságai

Üdvözöljük az F# 11 világában! Ez a kiadás segít elkülöníteni a tartományi modelleket, újrafelhasználni az általános algoritmusokat, összehangolni az aszinkron munkát, és csökkenteni a fordító memóriahasználatát.

Köszönjük az F#-közösségnek az e változások alapjául szolgáló javaslatokat, megvalósításokat, felülvizsgálatokat és teszteket. A közreműködői hivatkozások az alábbi elemekhez kapcsolódnak.

Note

A példákhoz F# 11 fordító és FSharp.Core 11 szükséges az itt bemutatott funkciókkal. A .NET 11.0.100 SDK-ban az F# 11.2 funkcióihoz <LangVersion>preview</LangVersion> szükséges; lásd: nyelvi beállítások és tervezett SDK-támogatás. A futtatókörnyezet-aszinkron példa további futtatókörnyezeti követelményekkel rendelkezik; előfordulhat, hogy egy korábbi .NET 11 SDK nem tartalmaz minden változást.

Rekordszórások: eltérő modellek ismétlődő leképezések nélkül

A tárolási és alkalmazásmodellek megkülönböztetése érdekében a rekordkiterjesztések ismételt deklarációk nélkül használják újra a megosztott mezőket. Itt a modellek megosztják az ügyfelek adatait, de különböző azonosítótípusokat használnak:

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 meződeklarációkat ad hozzá. A határnál másolja az értékeket, ...row míg az átalakítás elemzi az azonosítót:

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

A cél megtartja a megosztott részleteket, lecseréli Id, és kizárja a(z) InternalNote elemet. Guid.TryParse, nem az oldalpár, hanem az azonosítót ellenőrzi; a helytelen formátumú bemenet Error-t eredményez.

Ez az alkalmazásréteg-leképezési problémát kezeli. Az értékspreadek névvel ellátott és névtelen rekordokat is kombinálnak; a típusspreadek csak névvel ellátott rekordokat hoznak létre. A későbbi mezők felülírják a korábbiakat, a források pedig egyszer, meghatározott sorrendben futnak le. Lásd a rekordterítő operátorok című témakört az összeállítási példákkal és a közös fejlődés kockázataival kapcsolatban.

Köszönet @brianrourkeboll részére a rekord spreadekért.

Bővítménytagok általános algoritmusokhoz, operátorokhoz és n-esekhez

Az algoritmus meglévő típusok közötti megosztásához az F# 11.2 lehetővé teszi, hogy a bővítménytagok kielégítsék a statikusan feloldott típusparaméter (SRTP) korlátozásait. Egy átalakítás például burkolótípusok nélkül is kezelhet listákat és opciókat.

Note

A .NET 11.0.100 SDK-ban engedélyezze a(z) <LangVersion>preview</LangVersion> elemet ott, ahol a bővítmények feloldása történik, és nyissa meg a moduljukat. Lásd a könyvtár követelményeit.

Átalakítás megosztása listák és beállítások között

A kódtár map elemének hatókörében az alkalmazás megtisztíthatja bármelyik reprezentációt, módosíthatja az elemtípusokat, és átalakításokat állíthat össze:

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]

A Structural.Adapters adapterei ismert műveleteket használnak, ahelyett hogy új tárolók használatát kényszerítenék ki:

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

A Structural.Algorithms megosztott függvény a bemeneti típustól egy statikus Map tagot kér:

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

^Input és ^Output statikusan feloldott típusok. Minden egyes konkrét hívásnál a fordítóprogram kiválasztja a megkövetelt szignatúrával rendelkező tagot. A bővítmények nem adnak hozzá interfészimplementációt. A teljes alkalmazás mindkét modult megnyitja.

Operátorok, környezet és kimeneti típusok

Ugyanez a mechanizmus támogatja a tuple-operatorokat és a környezettovábbítást, valamint a kimenetvezérelt konstrukciót. A natív tuple-fejlécekhez az F# 11.2 funkciót engedélyezni kell a deklarációiknál, valamint a bővítménymegoldási híváshelyeken is.

Egy külön kiegészítés lehetővé teszi, AllowOverloadOnReturnTypehogy egy factory deklarálja a túlterheléseket a visszatérési típusaik alapján megkülönböztetve:

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"

Ehhez a Core 11 attribútumhoz nincs szükség előnézetre. Az eredményannotáció túlterhelt függvényváltozatot jelöl ki; nélküle a hívás továbbra is nem egyértelmű. Lásd: Kiválasztás visszatérési típus szerint.

Köszönjük @gusty-nak az általános programozási támogatás kiterjesztését.

.NET integráció és a fejlesztési élmény

A közvetlen delegáltak megőrzik a kezelőobjektum valódi kontraktusát

A kezelőnevek és kötési attribútumok keretrendszerek számára való felfedéséhez az F# 11 közvetlenül egy kompatibilis elnevezett függvényt célozhat meg burkoló helyett. ASP.NET Core Minimális API-k ezután elolvashatják az útvonal-, szolgáltatás- és fejlécszerződést:

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)

Tekintse meg a futtatható projektet és az építési/kompatibilitási szabályokat. A rögzítésekhez és az argumentumok kiigazításához továbbra is burkoló szükséges.

Köszönet a közvetlen meghatalmazottak @kerams.

XML-dokumentáció újrafelhasználása

Ha egyszer szeretné fenntartani a megosztott API-megjegyzéseket, helyezze őket a következő helyre shared.xml:

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

Ha GenerateDocumentationFile engedélyezve van, az F# 11 kibővül <include> a kibocsátott tagbejegyzésekkel:

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

A belefoglalási implementáció mindkét leírást frissíti egy töredékből. Az eszközfeloldó külön-külön elérhetővé teszi az alapleírásokat <inheritdoc/>; a fordító ezt a címkét hagyja a dokumentáció felhasználói számára. Példákat és megoldási szabályokat az XML-dokumentációban talál.

Az FSharp.Core megosztott XML-dokumentációjának @T-Gro-nak köszönhetően.

Beágyazott függvények hibakeresése nem optimalizált buildekben

A beágyazott segédfüggvény argumentumainak vizsgálatához állítson be egy töréspontot a(z) let total elemre egy nem optimalizált buildben, majd lépjen be a(z) addFee függvénybe. Az F# 11 ezekben a verziókban mostantól a meghívható metódusokat részesíti előnyben:

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

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

Az optimalizált buildek megtartják az inlininget. Egyes szerkezeteknek továbbra is bővülniük kell. A korlátokkal és a --always-inline vezérlővel kapcsolatban lásd: Soron belüli függvények hibakeresése.

Aszinkron és feladatkoordináció

Az F#-munkafolyamatok tevékenységalapú API-kkal való összekapcsolásához az FSharp.Core 11 megszakításkezelő hidakat, korlátozott méretű kötegeket és eredményátalakításokat ad hozzá.

Kösse a függőben lévő I/O-t, ne csak az eredménygyűjteményhez

A nagy mennyiségű olvasás több munkát indíthat el, mint amennyi egy alkalmazásnál folyamatban lehet. Annak eldöntéséhez, hogy mikor kezdődik a feladatalapú munka, a koordinátornak factory-kra van szüksége, nem pedig a már megkezdett feladatokra:

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

Legfeljebb négy olvasási művelet fut egyszerre; az eredmények megtartják a bemeneti sorrendet. A meghiúsulás a megadott tokeneken keresztül testvértörlést kér. Ez korlátozza az egyidejűséget, nem pedig a másodpercenkénti kéréseket. A szekvenciális és unit eredményű változatokért lásd: Aszinkron és taskfüggvények.

Őrizze meg a megszakítást és a mögöttes kivételt a határon át

Amikor egy munkafolyamat külső munkát indít, a tokennek ezzel a munkával együtt kell továbbhaladnia. Async.StartTaskImmediate átadja a futó munkafolyamat tokenjét a feladatgyárnak, és megvárja a befejezést:

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
    }

Egyetlen feladat hibája jut el ehhez a kezelőhöz mögöttes FileNotFoundException formájában. Már létrehozott feladat vagy értéket visszaadó feladat esetén használja a(z) Async.Await elemet; a régebbi Async.AwaitTask megtartja a kivételekbe csomagoló működését.

A feladatalapú hívók számára Task.startAsyncImmediate token workflow biztosítja a reverse bridge-et. Az első aszinkron felfüggesztésig a hívó szálon kezdődik.

Illeszkedő segédek használata rövid átalakításokhoz

Ha egy eredményt teljes számítási kifejezés nélkül szeretne átalakítani, használja a hozzá tartozó segédfüggvényeket a(z) Microsoft.FSharp.Control elemben. Például olvassa el a fájlt, kérdezze le a hosszát, és adjon vissza egy általános hibát a következő módon Error:

open System.IO
open System.Threading

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

Az Ok 8-t tartalmazó fájl Report 1-t eredményez; a hiányzó fájl egy Error-t ad, amely FileNotFoundException-at tartalmaz. A megszakítás továbbra is továbbterjed. Async és megfelelő bind, map és hibakezelő segédfüggvényeket kínál; lásd: ValueTask. Használjon számításkifejezést elágazásokhoz vagy több egymástól függő értékhez.

A ValueTask-segítőknek a Core netstandard2.1 vagy net10.0 buildjére és egyszeri felhasználásra van szükségük.

Köszönet @bartelink-nek az aszinkron/feladat API-hoz nyújtott hozzájárulásokért.

Újradobás aszinkron hibajelentés után

Az operatív naplózásnak anélkül kell információt hozzáadnia, hogy módosítaná, mit kezel a hívó, vagy elrejtené, hol kezdődött a hiba. A számítási-kifejezéskezelők most már akkor is használhatják a reraise () elemet, miután await-olják a naplózót:

Note

A számítási kifejezés újradobása egy F# 11.2-es funkció. A .NET 11.0.100 SDK-ban engedélyezze a(z) <LangVersion>preview</LangVersion> elemet.

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 ()
    }

A sikeres bejelentkezés után ugyanaz a kivétel és az eredeti dobáshely adatai terjednek tovább. A naplózó hibája lecserélheti az eredetit. Ez az aszinkron megfigyelhetőségi igényt kezeli futtatásidejű aszinkronitás nélkül. Lásd: Újradobás számítási kifejezésből.

Köszönjük @xperiandri támogatását.

Az FSharp.Core modern .NET

Ha modern API-kat szeretne használni a régebbi célok elvetése nélkül, az FSharp.Core hozzáadja net10.0 a netstandard2.0netstandard2.1. A NuGet a kompatibilis verziót választja ki. A frissített eszközcsomagolás a feladatalapú aszinkron felszabadítást is lehetővé teszi az F# Interactiveban, mint a lefordított projektekben. Lásd a FSharp.Core célkeretrendszereit.

@T-Gro-ért A modern Core célzásnak és SDK-eszközcsomagolásnak köszönhetően.

Futásidejű aszinkronitás összeállítók készítőinek

A builderek szerzői engedélyezhetik, hogy a futtatókörnyezet a fordító által létrehozott állapotgépek használata helyett felfüggessze és folytassa a megjelölt metódusokat. A kísérleti kódtár által definiált builder számítási kifejezésként teszi elérhetővé a mechanizmust:

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

Note

Ez a kísérleti builder az F# 11.2 egyik olyan funkcióját használja, amely lehetővé teszi az inline implementáció meghatározását vagy kibontását; a .NET 11.0.100 SDK-ban engedélyezze az előzetes funkciókat. Emellett a Core 11 buildjéhez net10.0 és hivatkozásaihoz, valamint egy futtatókörnyezethez is szükség van, amely támogatja a futtatókörnyezetet. Az ellenőrzött futtatókörnyezet-/referencia-kombináció itt a .NET 11 RC1. Csak .NET 10-et célozni nem elegendő. Lásd Futtatókörnyezeti aszinkron kísérletek.

Az FSharp.Core nem tartalmazza ezt a mintát általános célú builderként. A szokásos task és async kód nem vált automatikusan rá, és a már lefordított, feladatvisszaadó API-k hívóinak nincs eleve szükségük previewra.

Köszönet @majocha-nak a futásidejű aszinkronért.

Natív AOT-barát szöveg és kétdimenziós tömbök

A natív AOT támogatásához az F# 11 eltávolítja a reflexióalapú formázást és a dinamikus foglalási követelményeket a gyakoribb szöveg- és tömbműveletekből.

Ha már rendelkezik sztringgel, nincs szükség egy "%s" formátumra csak azért, hogy leírja. Az új Core-függvények, a print és a printn, értékeket fogadnak el az F# string-konverzióján keresztül; a printn új sort fűz hozzá:

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

A sztringinterpoláció több esetben használ közvetlen átalakítást és String.Concat-t. A(z) {total:F2} típusú .NET-formázási helyőrzők invariáns formázást használnak, és boxingolják az értéket; a(z) %A és a pontosságmegadást tartalmazó printf-formák esetében továbbra is szükség lehet printf-formázásra. Válasszon egy konkrét kultúrát a lokalizált kimenethez.

A --reflectionfreerekordok és az uniók szintén generált ToString kimenetet kapnak. A kulturális példákat és a támogatott útvonalakat lásd a Interpoláció és natív AOT című témakörben.

Köszönet @bbatsov-nak a nyomtatási függvényekért és @charlesroddie-nak az interpoláció megvalósításáért.

A szokásos, nulla alapú rács műveleteinek megtartása

Egy nullaindextől kezdődő rácsnak nem szabadna helyettesítő Array2D modult igényelnie pusztán azért, hogy elkerülje a dinamikuskód-allokációs útvonalat. A frissített Core-implementációk támogatják a jól ismert inicializálást és átalakítást:

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, init és rebase használják a nulla alapú foglalási útvonalat a Core-buildekben. A korlátmegőrző map, mapi és copy használhat egy újabb .NET foglalási API-t a net10.0 buildben; a .NET Standard buildek megtartják tartalék megoldásukat. A nem nulla alapú tömbök nincsenek elavultként megjelölve, de a natív foglalási korlátozások megmaradnak.

A lefoglalási útvonalakról és a fennmaradó korlátozásokról lásd a kétdimenziós tömböket és a natív AOT-t. Ellenőrizze a teljes alkalmazás közzétételi diagnosztikáját.

Köszönet @T-Gro-nak az Array2D fejlesztésekért.

Kevesebb megőrzött memória és kevesebb ismétlődő munka

Az F# 11 csökkenti az ismétlődő fordítóelemzést és néhány alkalmazásfoglalást. Ezek különböző számítási feladatokat érintenek.

Hivatkozott kódtárak megosztása projektek között

A megosztott bináris függőségek ismétlődő példányainak elkerülése érdekében a nyelvi szolgáltatás megoszthatja az alkalmas importált függőségi gráfokat a projektek között.

Az implementáló PR memória-megtartási kísérletében a tízprojektből álló ReSharper.FSharp-megoldás 1547,2 MB-ról 1002,2 MB-ra csökkent a megosztás engedélyezésével. Ez az adott terhelés be/ki jellegű mérése, nem pedig általános F# 10-ről 11-re csökkentés. A közös bináris hivatkozások helyett a projekthivatkozások által dominált megoldások sokkal kisebb nyereséget láthatnak.

A metaadatok beolvasása és a soron belüli specializáció szintén csökkentik az átmeneti allokációkat, amelyek egy külön mérőszámot képeznek. A hatókörrel és vezérlőkkel kapcsolatban lásd: Tooling performance.

Köszönjük @auduchinok-nak/nek a megosztott kódtárak importálását.

Önálló munka fordítása párhuzamosan alapértelmezés szerint

A párhuzamos fordítás alapértelmezés szerint engedélyezve van, a determinisztikus kimenet és a forrásrend szabályai megmaradnak. A túlterheléses gyorsítótárazás és az őrfeltételes minták fejlesztései elkerülik az egyéb ismételt munkát. A Visual Studio/FCS emellett csökkenti a változatlan dokumentumok elemzését, és korlátozza a párhuzamos ellenőrzések számát; lásd: Az eszközök teljesítménye.

A visszahívás és az üres eredményű lefoglalási többletterhelés elkerülése

A kiválasztott List és Array függvények mostantól inline callbackeket támogatnak. A saját beágyazott segítőidhez OptimizeClosureIfNotInlined egyszer egy átlátszatlan, curried visszahívást adaptál:

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

Más lefoglalások is megmaradhatnak. A követelményekért lásd: A kurizott callbackek optimalizálása.

Az üres eredmények is a sikeres működés gyakori jelei. Az FSharpLintet motiváló munkaterhelés ismételten olyan szabályokat kombinált, amelyek nem adtak figyelmeztetést. A Core mostantól még több műveletben újrafelhasználja a megosztott üres tömböket:

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

A módosítás eltávolítja a redundáns üres tömbobjektumokat, nem pedig a folyamat minden foglalását. Ne hagyatkozzon arra, hogy a különálló üres eredmények különálló referencia-identitással rendelkeznek.

@T-Gro A beágyazott gyűjtemények visszahívásainak és a megosztott üres tömböknek köszönhetően.

Pontosabb API-hívások és mindennapi kényelem

Nevek megkövetelése, ha a létrehozott paramétersorrend megváltozhat

A generált API-k csendes paramétersorrend-módosításokkal szembeni védelméhez a szerzőnek elnevezett argumentumokat kell megadnia:

Note

Az enforcement egy F# 11.2-funkció; a .NET 11.0.100 SDK-ban engedélyezze az előnézetet. Rendelkezésre álló System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute definícióra is szükség van. A teljes minta egy kompatibilitási definíciót biztosít azokhoz a hivatkozásokhoz, amelyekből ez hiányzik.

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.

A deklaráció átrendezése megőrzi az elnevezett hívás eredményét; 0.75Ma rendszer elutasít egy pozícióhívást. A generált API-használati esethez a szolgáltatónak ki kell adnia az annotációt.

Már burkolt általános paraméter felismerése

A meglévő tevékenységek egyszerű eredményértékbe csomagolásának elkerülése érdekében az új végső megoldás túlterhelési szabálya 'T inkább a csupasz Task<'T> értéket részesíti előnyben:

Note

A legkonkrétabb tiebreaker és a túlterhelési prioritás működése az F# 11.2 funkciói; a .NET 11.0.100 SDK-ban engedélyezze az előzetes funkciókat.

open System.Threading.Tasks

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

Az eredmény ValueTask<int>, nem ValueTask<Task<int>>. A szabály korlátait lásd: Overload selection.

Új túlterhelés előnyben részesítése a régi hozzáférési pont eltávolítása nélkül

Előfordulhat, hogy egy könyvtár megtart egy régi diagnosztikai módszert a meglévő bináris fájlokhoz, miközben hozzáad egy túlterhelt függvényváltozatot, amely tartalmazza a hívó forráshelyét. OverloadResolutionPriorityAttribute egyértelművé teszi ezt a beállítást az újonnan lefordított hívók számára:

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.

A változatlan hívás a sorérzékeny túlterhelt változatot választja ki. A hatókörre és kompatibilitásra vonatkozó szabályokat lásd itt: túlterhelés kiválasztása.

A .NET feltételes nullkezelési ígéreteinek használata

Ha a fájl elérési útja nem null értékű, a Path API nullázható eredménye nem kényszeríthet olyan ellenőrzésre, amelyet a saját szerződése eleve kizár. Az F# 11 a meglévő .NET NotNullIfNotNull garanciát veszi figyelembe: a nem nullként megjelölt bemenet nem null eredményt jelent.

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

A(z) <Nullable>enable</Nullable>engedélyezése. A null értékű bemenetek továbbra is null értékű kezelésre szorulnak; lásd a feltételes visszaküldési szerződéseket. A korlátozás nélküli C# T? API-khoz használja a külön Unchecked.withNull segítőt.

Köszönet @kerams-nak a feltételes nullázhatóság támogatásáért.

Kisebb nyelvi fejlesztések

Ha .NET konstruktorszintaxissal szeretne rekordtípust példányosítani, használja az F# 11.2 rekordkonstruktorokat:

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

A névvel ellátott argumentumok elkerülik a mezőrendtől való függőséget. Köszönjük, @charlesroddie-nak ezt a funkciót.

Feltételes forrás esetén a #elif elkerüli a beágyazott #if csoportokat:

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

System.Console.WriteLine(dispatchTimeZone)

Az első egyező ág nyer. A mintaprojekt definiálja a(z) EU_WAREHOUSE elemet, és kiírja a(z) Europe/Paris elemet; ha egyik szimbólum sincs definiálva, a kimenet UTC.

Kapcsolódó eszközökhöz és interop esetekhez:

Frissítés előtt

A frissítéskor tartsa szem előtt ezeket a jelentős változásokat:

Érintett kód Action
Tárak fájlokkal .fsi Kényszerített szerződésattribútumok másolása aláírásokra; a kihagyások mostantól FS3888-hibák.
Bővítmények által támogatott beágyazott könyvtárak Tekintse át a kikövetkeztetett aláírásokat és a gyártói/fogyasztói követelményeket. Az F# 11.2 típuskövetkeztetése általánosabb korlátozásokat is megtarthat.
Példánymetódus-meghatalmazottak Ellenőrizze a fogadó objektumokat a közvetlen konstrukció előtt, amikor most már null értékű fogadó objektum miatti hibák jelentkeznek.
Lokalizált, interpolált kimenet A .NET-formátumhelyek invariáns kultúrát használnak. Válasszon megadott kultúrát, ha honosított kimenet a cél.
Egyéni építők final do! A kötések és egységek kiegészítése; a final do! már nem választja ki a final-forwarding tagokat.
Befejezetlen let! Adja hozzá a következő számítási törzset; a hiányos kötés mostantól közvetlenebb diagnosztikai üzenetet is ad.
Régebbi nyelvi beállítások A támogatott számozott verziók az F# 8-nál kezdődnek; a nem szigorú behúzási mód el lett távolítva. Lásd : Nyelvi verzió és behúzás.
Enum bitenkénti műveletek egész típusú háttértárral rendelkező enumokat használjon; a rendszer elutasítja az érvénytelen háttértípusokat.