Delen via


Wat is nieuw in F# 10

Met F# 10 kunt u verschillende verbeteringen aanbrengen in de F#-taal, FSharp.Core-bibliotheek en hulpprogramma's. Deze versie is een verfijningsrelease die is gericht op duidelijkheid, consistentie en prestaties, met kleine maar zinvolle verbeteringen die uw dagelijkse code leesbaarder en robuuster maken. F# 10 wordt geleverd met .NET 10 en Visual Studio 2026.

U kunt de nieuwste .NET SDK downloaden via de .NET-downloadpagina.

Get started

F# 10 is beschikbaar in alle .NET Core-distributies en Visual Studio-hulpprogramma's. Zie Aan de slag met F# voor meer informatie.

Bereik van waarschuwingsonderdrukking

U kunt nu waarschuwingen in specifieke secties van uw code onderdrukken met behulp van de nieuwe #warnon richtlijn. Dit werkt samen met de bestaande #nowarn richtlijn om u nauwkeurige beheersing te geven over welke waarschuwingen waar van toepassing zijn.

Voorheen, wanneer u #nowarn gebruikte, schakelde het een waarschuwing uit voor de rest van het bestand, waardoor legitieme problemen elders konden worden onderdrukt. Laten we eens kijken naar een motiverend voorbeeld:

// We know f is never called with None.
let f (Some a) =    // creates warning 25, which we want to suppress
    // 2000 loc, where the incomplete match warning is beneficial

Als u boven de functiedefinitie toevoegt #nowarn 25 , wordt FS0025 uitgeschakeld voor de gehele rest van het bestand.

Met F# 10 kunt u nu de exacte sectie markeren waar u de waarschuwing wilt onderdrukken:

#nowarn 25
let f (Some x) =    // FS0025 suppressed
#warnon 25
    // FS0025 enabled again

Als een waarschuwing daarentegen globaal is uitgeschakeld (bijvoorbeeld via een compilervlag), kunt u deze lokaal inschakelen met #warnon. Deze richtlijn is van toepassing totdat er een overeenkomend #nowarn is of tot het einde van het bestand.

Belangrijke compatibiliteitsnotities:

Deze functie bevat verschillende wijzigingen die de consistentie van #nowarn/#warnon richtlijnen verbeteren. Dit zijn belangrijke wijzigingen:

  • De compiler staat geen instructies meer met meerdere regels en lege waarschuwingen toe.
  • De compiler staat geen witruimte meer toe tussen # en nowarn.
  • U kunt geen tekenreeksen met drie puntjes, geïnterpoleerd of letterlijke tekenreeksen gebruiken voor waarschuwingsnummers.

Scriptgedrag is ook gewijzigd. Voorheen, toen u ergens in een script een #nowarn instructie toevoegde, werd deze toegepast op de hele compilatie. Nu komt het gedrag in scripts overeen met dat in .fs bestanden, alleen van toepassing tot het einde van het bestand of een bijbehorende #warnon.

Met deze functie wordt RFC FS-1146 geïmplementeerd.

Toegangsmodificaties voor automatische eigenschapstoegangen

Een veelvoorkomend patroon in objectgeoriënteerde programmering is het maken van openbaar leesbare maar privé veranderlijke toestand. Voor F# 10 hebt u expliciete eigenschapsyntaxis nodig met back-upvelden (verborgen variabelen waarmee de werkelijke eigenschapswaarden worden opgeslagen) om dit te bereiken, waardoor terugkerende code wordt toegevoegd:

type Ledger() =
    [<DefaultValue>] val mutable private _Balance: decimal
    member this.Balance with public get() = this._Balance and private set v = this._Balance <- v

Met F# 10 kunt u nu verschillende toegangsmodificatoren toepassen op individuele eigenschapaccessors. Hiermee kunt u verschillende toegangsniveaus opgeven voor de getter en setter van een eigenschap, waardoor het patroon veel eenvoudiger wordt:

type Ledger() =
    member val Balance = 0m with public get, private set

U kunt een toegangsmodificator plaatsen vóór de naam van de eigenschap (van toepassing op beide accessors) of vóór afzonderlijke accessors, maar niet beide tegelijkertijd.

Houd er rekening mee dat deze functie niet wordt uitgebreid naar handtekeningbestanden (.fsibestanden). De juiste handtekening voor het Ledger bovenstaande voorbeeld is:

type Ledger() =
    member Balance : decimal
    member private Balance : decimal with set

Met deze functie wordt RFC FS-1141 geïmplementeerd.

Optionele parameters voor ValueOption

U kunt nu een op struct gebaseerde ValueOption<'T> weergave gebruiken voor optionele parameters. Wanneer u het [<Struct>] kenmerk toepast op een optionele parameter, gebruikt de compiler ValueOption<'T> in plaats van het verwijzingstype option. Dit voorkomt heap-toewijzingen (geheugen toegewezen aan de beheerde heap waarvoor garbage collectie is vereist) voor de optie-wrapper, wat voordelig is in prestatiekritieke code.

Eerder gebruikte F# altijd het heap-toegewezen option type voor optionele parameters, zelfs wanneer de parameter afwezig was:

// Prior to F# 10: always uses reference option
type X() =
    static member M(?x : string) =
        match x with
        | Some v -> printfn "Some %s" v
        | None -> printfn "None"

In F# 10 kunt u het [<Struct>] kenmerk gebruiken om gebruik te maken van de struct-ondersteunde ValueOption:

type X() =
    static member M([<Struct>] ?x : string) =
        match x with
        | ValueSome v -> printfn "ValueSome %s" v
        | ValueNone -> printfn "ValueNone"

Dit elimineert heap-toewijzingen wanneer het argument afwezig is, wat nuttig is in prestatiekritieke code.

Kies deze optie op basis van struct voor kleine waarden of vaak samengestelde typen waarbij de toewijzingsdruk van belang is. Gebruik de standaardverwijzingsbasis option wanneer u afhankelijk bent van bestaande helpers voor patroonkoppeling, verwijzingssemantiek nodig hebt of wanneer het prestatieverschil te verwaarlozen is. Deze functie versterkt pariteit met andere F#-taalconstructies die al worden ondersteund ValueOption.

Ondersteuning voor tail-call in berekeningsexpressies

F# 10 voegt optimalisaties voor tail-call toe voor rekenexpressies. Bouwers van berekeningsexpressies kunnen zich nu voor deze optimalisaties aanmelden door speciale methoden te implementeren.

Wanneer de compiler rekenexpressies vertaalt in reguliere F#-code (een proces met de naam desugaring), herkent deze wanneer een expressie zoals return!, yield!of do! in een staartpositie wordt weergegeven. Als uw opbouwfunctie de volgende methoden biedt, stuurt de compiler deze aanroepen door naar geoptimaliseerde toegangspunten:

  • ReturnFromFinal - opgeroepen voor een staart return! (valt terug naar ReturnFrom indien afwezig)
  • YieldFromFinal - opgeroepen voor een staart yield! (valt terug naar YieldFrom indien afwezig)
  • Voor een terminal do! geeft de compiler de voorkeur aan ReturnFromFinal, dan YieldFromFinal, voordat hij terugvalt naar het normale Bind-pad.

Deze *Final leden zijn optioneel en bestaan uitsluitend om optimalisatie mogelijk te maken. Bouwers die deze leden niet voorzien, behouden hun bestaande semantiek ongewijzigd.

Voorbeeld:

coroutine {
    yield! subRoutine() // tail position -> YieldFromFinal if available
}

In een niet-staartpositie:

coroutine {
    try
        yield! subRoutine() // not tail -> normal YieldFrom
    finally ()
}

Belangrijke compatibiliteitsnotitie:

Deze wijziging kan fouten veroorzaken als een opbouwfunctie voor berekeningsexpressies al leden met deze namen definieert. In de meeste gevallen blijven bestaande bouwers werken zonder aanpassingen wanneer ze zijn gecompileerd met F# 10. Oudere compilers negeren de nieuwe *Final methoden, dus bouwers die compatibel moeten blijven met eerdere compilerversies, mogen niet aannemen dat de compiler deze methoden aanroept.

Met deze functie wordt RFC FS-1330 geïmplementeerd.

Getypte bindingen in berekeningsexpressies zonder haakjes

Met F# 10 wordt de vereiste voor haakjes verwijderd bij het toevoegen van typeaantekeningen aan rekenexpressiebindingen. U kunt nu typeaantekeningen toevoegen aan let!, use!en and! bindingen met dezelfde syntaxis als gewone let bindingen.

Voorheen moest u haakjes gebruiken voor het typen van aantekeningen:

async {
    let! (a: int) = fetchA()
    and! (b: int) = fetchB()
    use! (d: MyDisposable) = acquireAsync()
    return a + b
}

In F# 10 kunt u aantekeningen schrijven zonder haakjes:

async {
    let! a: int = fetchA()
    and! b: int = fetchB()
    use! d: MyDisposable = acquireAsync()
    return a + b
}

Toestaan _ in use! bindingen

U kunt nu het verwijderingspatroon (_) gebruiken in use! bindingen binnen berekeningsexpressies. Hiermee wordt het gedrag van use! in lijn gebracht met de normale use bindingen.

Voorheen heeft de compiler het verwijderingspatroon in use! bindingen geweigerd, waardoor u wegwerp-id's moet maken:

counterDisposable {
    use! _ignored = new Disposable()
    // logic
}

In F# 10 kunt u het verwijderingspatroon rechtstreeks gebruiken:

counterDisposable {
    use! _ = new Disposable()
    // logic
}

Dit verduidelijkt de intentie bij het binden van asynchrone resources waarvan de waarden alleen nodig zijn voor levensduurbeheer.

Pseudo-geneste modules in typen weigeren

De compiler genereert nu een fout wanneer u een module declaratie plaatst die is ingesprongen op hetzelfde structurele niveau binnen een typedefinitie. Hierdoor wordt de structurele validatie aangescherpt om de plaatsing van misleidende modules binnen typen te weigeren.

Voorheen accepteerde de compiler declaraties module die binnen typedefinities ingesprongen waren, maar creëerde deze modules feitelijk als naastgelegen modules in plaats van ze in het type te nesten.

type U =
    | A
    | B
    module M = // Silently created a sibling module, not nested
        let f () = ()

Met F# 10 genereert dit patroon fout FS0058, waardoor u uw intentie moet verduidelijken met de juiste moduleplaatsing:

type U =
    | A
    | B

module M =
    let f () = ()

Verouderingswaarschuwing voor weggelaten seq

De compiler waarschuwt u nu voor lege reeksexpressies die de seq opbouwfunctie weglaten. Wanneer u lege bereik-accolades zoals { 1..10 } gebruikt, ziet u een depreciatiewaarschuwing die u aanmoedigt om de expliciete seq { ... } vorm te gebruiken.

Historisch gezien stond F# een speciale syntaxis toe voor 'sequentiebegrip lite', waarbij u het seq trefwoord kon weglaten.

{ 1..10 } |> List.ofSeq  // implicit sequence, warning FS3873 in F# 10

In F# 10 waarschuwt de compiler voor dit patroon en moedigt de expliciete vorm aan:

seq { 1..10 } |> List.ofSeq

Dit is momenteel een waarschuwing, geen fout, waardoor u tijd hebt om uw codebase bij te werken. Als u deze waarschuwing wilt onderdrukken, gebruikt u de NoWarn-eigenschap in uw projectbestand of de #nowarn-instructie lokaal en geeft u het waarschuwingsnummer door: 3873.

Het expliciete seq formulier verbetert de helderheid en consistentie van code met andere rekenexpressies. In toekomstige versies van F# kan dit een fout optreden, dus we raden u aan de expliciete syntaxis te gebruiken wanneer u uw code bijwerkt.

Met deze functie wordt RFC FS-1033 geïmplementeerd.

Het afdwingen van attributen op doelwitten

F# 10 dwingt validatie van kenmerkdoel af voor alle taalconstructies. De compiler valideert nu dat kenmerken alleen worden toegepast op hun beoogde doelen door te controleren AttributeTargets op let-gebonden waarden, functies, samenvoegcases, impliciete constructors, structs en klassen.

Voorheen stond de compiler stilzwijgend toe dat u kenmerken verkeerd toepaste op incompatibele doelen. Dit heeft subtiele bugs veroorzaakt, zoals testkenmerken die worden genegeerd wanneer u bent vergeten () een functie te maken:

[<Fact>]
let ``this is not a function`` = // Silently ignored in F# 9, not a test!
    Assert.True(false)

In F# 10 dwingt de compiler kenmerkdoelen af en wordt een waarschuwing weergegeven wanneer kenmerken onjuist worden toegepast:

[<Fact>]
//^^^^ - warning FS0842: This attribute cannot be applied to property, field, return value. Valid targets are: method
let ``this is not a function`` =
    Assert.True(false)

Belangrijke compatibiliteitsnotitie:

Dit is een brekende verandering die eerder onopgemerkte problemen in uw codebase kan blootleggen. De vroege fouten verhinderen testdetectieproblemen en zorgen ervoor dat kenmerken zoals analyse en decorators van kracht worden zoals bedoeld.

Ondersteuning voor and! in taakexpressies

U kunt nu meerdere taken gelijktijdig afwachten met behulp van and!taakexpressies. Het gebruik task is een populaire manier om te werken met asynchrone werkstromen in F#, met name wanneer u interoperabiliteit met C# nodig hebt. Tot nu toe was er echter geen beknopte manier om meerdere taken tegelijk in een computationele expressie op te wachten.

Misschien bent u begonnen met code die sequentieel op berekeningen wachtte:

// Awaiting sequentially
task {
    let! a = fetchA()
    let! b = fetchB()
    return combineAB a b
}

Als u ze vervolgens tegelijkertijd wilt laten wachten, gebruikt u meestal Task.WhenAll.

// Use explicit Task combinator to await concurrently
task {
    let ta = fetchA()
    let tb = fetchB()
    let! results = Task.WhenAll([| ta; tb |])
    return combineAB ta.Result tb.Result
}

In F# 10 kunt u gebruiken and! voor een meer idiomatische benadering:

task {
    let! a = fetchA()
    and! b = fetchB()
    return combineAB a b
}

Hiermee worden de semantiek van de gelijktijdige versie gecombineerd met de eenvoud van de sequentiële versie.

Deze functie implementeert F#-taalsuggesties #1363 en wordt geïmplementeerd als aanvulling op de FSharp.Core bibliotheek. De meeste projecten krijgen automatisch de nieuwste versie van de compiler van FSharp.Core, tenzij ze expliciet een versie vastzetten. In dat geval moet u deze bijwerken om deze functie te kunnen gebruiken.

Betere trimmen als standaard

F# 10 verwijdert een langstaand beetje wrijving met het bijsnijden van F#-assembly's. Bijsnijden is het proces voor het verwijderen van ongebruikte code uit uw gepubliceerde toepassing om de grootte te verkleinen. U hoeft een ILLink.Substitutions.xml bestand niet meer handmatig bij te houden om grote resource-blobs met F#-metagegevens te verwijderen (handtekening- en optimalisatiegegevens die de compiler gebruikt, maar die uw toepassing niet nodig heeft tijdens runtime).

Wanneer u publiceert met inkorten ingeschakeld (PublishTrimmed=true), genereert de F#-build nu automatisch een ingesloten vervangingsbestand dat specifiek is voor deze uitsluitend op tooling gerichte F#-resources.

Voorheen moest u dit bestand handmatig onderhouden om de metagegevens te verwijderen. Deze extra onderhoudslast vormde en was gemakkelijk te vergeten.

Het resultaat is standaard een kleinere uitvoer, minder terugkerende code die moet worden onderhouden en één minder onderhoudsrisico. Als u volledig handmatig beheer nodig hebt, kunt u nog steeds uw eigen vervangingsbestand toevoegen. U kunt de automatische generatie uitschakelen met de <DisableILLinkSubstitutions>false</DisableILLinkSubstitutions> eigenschap.

Parallelle compilatie in preview

Een spannende update voor F#-gebruikers die compilatietijden willen verminderen: de parallelle compilatiefuncties stabiliseren. Vanaf .NET 10 worden drie functies gegroepeerd onder de ParallelCompilation projecteigenschap: op grafieken gebaseerde typecontrole, parallelle il-codegeneratie en parallelle optimalisatie.

Met F# 10 wordt deze instelling standaard ingeschakeld voor projecten die gebruikmaken van LangVersion=Preview. We zijn van plan deze in te schakelen voor alle projecten in .NET 11.

Zorg ervoor dat u het eens probeert en ziet of het uw compilatie versnelt. Parallelle compilatie inschakelen in F# 10:

<PropertyGroup>
    <ParallelCompilation>true</ParallelCompilation>
    <Deterministic>false</Deterministic> <!-- Note: deterministic builds don't get the benefits of parallel compilation -->
</PropertyGroup>

Als u zich wilt afmelden terwijl u nog steeds andere preview-functies gebruikt, stelt u deze optie in ParallelCompilation op false:

<PropertyGroup>
    <LangVersion>Preview</LangVersion>
    <ParallelCompilation>false</ParallelCompilation>
</PropertyGroup>

Parallelle compilatie kan de compilatietijden voor projecten met meerdere bestanden en afhankelijkheden aanzienlijk verminderen.

Subsumption-cache typen

De compiler slaat nu typerelaties in de cache op om typedeductie te versnellen en de IDE-prestaties te verbeteren, met name bij het werken met complexe typehiërarchieën. Door resultaten van vorige subsamenvattingscontroles op te slaan en opnieuw te gebruiken, vermijdt de compiler redundante berekeningen die eerder compilatie en IntelliSense vertraagden.

De cache beheren:

In de meeste gevallen verbetert het type subsumption-cache de prestaties zonder enige configuratie. Als u echter meer geheugenvoetafdruk of een verhoogd CPU-gebruik ondervindt (vanwege onderhoudsmedewerkers voor cache), kunt u het cachegedrag aanpassen:

  • Als u de cache volledig wilt uitschakelen, stelt u <LangVersion>9</LangVersion> in het projectbestand in om terug te vallen op F# 9-gedrag.
  • Als u verwijdering van asynchrone cache wilt uitschakelen (waardoor de threaddruk toeneemt) en in plaats daarvan synchrone verwijdering wilt gebruiken, stelt u de FSharp_CacheEvictionImmediate=1 omgevingsvariabele in.