Partager via


Nouveautés de F# 10

F# 10 vous apporte plusieurs améliorations au langage F#, à la bibliothèque FSharp.Core et aux outils. Cette version est une version d’affinement axée sur la clarté, la cohérence et les performances, avec des améliorations petites mais significatives qui rendent votre code quotidien plus lisible et plus robuste. F# 10 est fourni avec .NET 10 et Visual Studio 2026.

Vous pouvez télécharger le dernier SDK .NET à partir de la page de téléchargements .NET.

Get started

F# 10 est disponible dans toutes les distributions .NET Core et les outils Visual Studio. Pour plus d’informations, consultez Prise en main de F#.

Suppression d’avertissement délimitée

Vous pouvez désormais supprimer des avertissements dans des sections spécifiques de votre code à l’aide de la nouvelle #warnon directive. Cela s'associe à la directive existante #nowarn pour vous donner un contrôle précis sur les avertissements qui s’appliquent où.

Précédemment, lorsque vous utilisiez #nowarn, cela désactivait un avertissement pour le reste du fichier, ce qui pouvait masquer des problèmes légitimes ailleurs. Examinons un exemple de motivation :

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

Si vous ajoutez #nowarn 25 au-dessus de la définition de fonction, elle désactive FS0025 pour le reste du fichier.

Avec F# 10, vous pouvez maintenant marquer la section exacte dans laquelle vous souhaitez supprimer l’avertissement :

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

À l’inverse, si un avertissement est désactivé globalement (par exemple, via un indicateur de compilateur), vous pouvez l’activer localement avec #warnon. Cette directive s’appliquera jusqu’à ce qu'un élément correspondant #nowarn soit trouvé ou jusqu’à la fin du fichier.

Remarques de compatibilité importantes :

Cette fonctionnalité comprend plusieurs modifications qui améliorent la cohérence des #nowarn/#warnon directives. Il s’agit de changements cassants :

  • Le compilateur n'autorise plus les directives de préavis multiligne et les directives vides.
  • Le compilateur n’autorise plus l’espace blanc entre # et nowarn.
  • Vous ne pouvez pas utiliser des chaînes entre triple guillemets, interpolées ou verbatim pour les numéros d’avertissement.

Le comportement du script a également changé. Auparavant, lorsque vous avez ajouté une #nowarn directive n’importe où dans un script, elle s’applique à l’ensemble de la compilation. Maintenant, son comportement dans les scripts correspond à celui dans les fichiers .fs, s'appliquant uniquement jusqu'à la fin du fichier ou jusqu'à un #warnon correspondant.

Cette fonctionnalité implémente RFC FS-1146.

Modificateurs d’accès sur les accesseurs de propriété automatique

Un modèle courant dans la programmation orientée objet consiste à créer un état lisible publiquement mais en privé mutable. Avant F# 10, vous avez besoin d’une syntaxe de propriété explicite avec des champs de stockage (variables masquées qui stockent les valeurs de propriété réelles) pour y parvenir, ce qui a ajouté du code répétitif :

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

Avec F# 10, vous pouvez désormais appliquer des modificateurs d’accès différents aux accesseurs de propriété individuels. Cela vous permet de spécifier différents niveaux d’accès pour le getter et setter d’une propriété, ce qui simplifie beaucoup le modèle :

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

Vous pouvez placer un modificateur d’accès avant le nom de propriété (s’appliquant aux deux accesseurs) ou avant les accesseurs individuels, mais pas les deux simultanément.

Notez que cette fonctionnalité ne s’étend pas aux fichiers de signature (.fsi). La signature correcte pour l’exemple Ledger ci-dessus est la suivante :

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

Cette fonctionnalité implémente RFC FS-1141.

ValueOption paramètres facultatifs

Vous pouvez maintenant utiliser une représentation en struct pour les paramètres facultatifs. Lorsque vous appliquez l’attribut [<Struct>] à un paramètre facultatif, le compilateur utilise ValueOption<'T> plutôt que le type de référence option . ** Cela évite les allocations de tas (mémoire allouée sur le tas managé nécessitant un garbage collection) pour le wrapper d'option, ce qui est bénéfique dans le code où la performance est cruciale.

Auparavant, F# utilisait toujours le type alloué sur le tas option pour les paramètres facultatifs, même lorsque le paramètre était absent :

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

Dans F# 10, vous pouvez utiliser l’attribut [<Struct>] pour tirer parti du struct -backed ValueOption:

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

Cela élimine les allocations de tas lorsque l’argument est absent, ce qui est bénéfique dans le code essentiel à la performance.

Choisissez cette option basée sur une structure pour les petites valeurs ou les types fréquemment construits, où la pression d’allocation compte. Utilisez la référence option par défaut lorsque vous vous appuyez sur des assistants de correspondance de modèles existants, que vous avez besoin d’une sémantique de référence, ou lorsque la différence de performances est négligeable. Cette fonctionnalité renforce la parité avec d’autres constructions de langage F# qui prennent déjà en charge ValueOption.

Prise en charge des appels de queue dans les expressions de calcul

F# 10 ajoute des optimisations de rappel de fin pour les expressions calculatoires. Les générateurs d’expressions de calcul peuvent désormais opter pour ces optimisations en implémentant des méthodes spéciales.

Lorsque le compilateur traduit des expressions de calcul en code F# standard (un processus appelé désugaring), il reconnaît quand une expression comme return!, yield!ou do! s’affiche à une position de queue. Si votre générateur fournit les méthodes suivantes, le compilateur achemine ces appels vers des points d’entrée optimisés :

  • ReturnFromFinal - appelé pour une queue return! (revient à ReturnFrom si absent)
  • YieldFromFinal - appelé pour une queue yield! (revient à YieldFrom si absent)
  • Pour un terminaldo!, le compilateur préfère ReturnFromFinal, puis YieldFromFinal, avant de revenir à la voie normale Bind

Ces *Final membres sont facultatifs et existent uniquement pour activer l’optimisation. Les générateurs qui ne fournissent pas ces membres conservent leur sémantique existante inchangée.

Par exemple:

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

Toutefois, dans une position non-queue :

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

Remarque de compatibilité importante :

Cette modification peut provoquer une rupture si un générateur d'expressions de calcul définit déjà des membres sous les mêmes noms. Dans la plupart des cas, les générateurs existants continuent de fonctionner sans modification lorsqu’ils sont compilés avec F# 10. Les compilateurs plus anciens ignorent les nouvelles *Final méthodes. Par conséquent, les générateurs qui doivent rester compatibles avec les versions antérieures du compilateur ne doivent pas supposer que le compilateur appelle ces méthodes.

Cette fonctionnalité implémente RFC FS-1330.

Liaisons typées dans les expressions de calcul sans parenthèses

F# 10 supprime la condition requise pour les parenthèses lors de l’ajout d’annotations de type aux liaisons d’expression de calcul. Vous pouvez maintenant ajouter des annotations de type sur let!, use!et and! des liaisons à l’aide de la même syntaxe que les liaisons ordinaires let .

Auparavant, vous deviez utiliser des parenthèses pour les annotations de type :

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

Dans F# 10, vous pouvez écrire des annotations de type sans parenthèses :

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

Autoriser _ dans use! les liaisons

Vous pouvez maintenant utiliser le modèle d’abandon (_) dans les liaisons de use! dans les expressions de calcul. Cela aligne le comportement de use! avec les liaisons standard use.

Auparavant, le compilateur rejetait le motif de rejet dans les liaisons use!, vous obligeant à créer des identificateurs jetables :

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

Dans F# 10, vous pouvez utiliser directement le modèle de rejet :

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

Cela permet de clarifier l’intention lors de la liaison de ressources asynchrones dont les valeurs sont uniquement nécessaires pour la gestion de la durée de vie.

Rejet des modules pseudo-imbriqués dans les types

Le compilateur génère maintenant une erreur lorsque vous placez une module déclaration en retrait au même niveau structurel à l’intérieur d’une définition de type. Cela renforce la validation structurelle pour rejeter le placement trompeur des modules au sein des types.

Auparavant, le compilateur acceptait module les déclarations indentées dans les définitions de type, mais il créait réellement ces modules au même niveau que le type plutôt que de les imbriquer au sein de celui-ci.

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

Avec F# 10, ce modèle génère l’erreur FS0058, vous obligeant à clarifier votre intention avec le placement approprié du module :

type U =
    | A
    | B

module M =
    let f () = ()

Avertissement de dépréciation pour l'élément seq omis

Le compilateur vous avertit maintenant des expressions de séquence nue qui omettent le seq générateur. Lorsque vous utilisez des accolades à portée nue, { 1..10 }vous verrez un avertissement de dépréciation vous invitant à utiliser le formulaire explicite seq { ... } .

Historiquement, F# a autorisé une syntaxe spéciale de « compréhension de séquence lite » dans laquelle vous pourriez omettre le seq mot clé :

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

En F# 10, le compilateur avertit de ce modèle et encourage le formulaire explicite :

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

Il s’agit actuellement d’un avertissement, et non d’une erreur, ce qui vous donne le temps de mettre à jour votre codebase. Si vous souhaitez supprimer cet avertissement, utilisez la propriété NoWarn dans le fichier de votre projet ou la directive #nowarn localement et spécifiez le numéro d'avertissement : 3873.

Le formulaire explicite seq améliore la clarté et la cohérence du code avec d’autres expressions de calcul. Les futures versions de F# peuvent entraîner cette erreur. Nous vous recommandons donc d’adopter la syntaxe explicite lorsque vous mettez à jour votre code.

Cette fonctionnalité implémente RFC FS-1033.

Mise en application de la cible d’attribut

F# 10 applique la validation de la cible d’attribut sur toutes les constructions de langage. Le compilateur valide désormais que les attributs ne sont appliqués qu’à leurs cibles prévues en vérifiant les valeurs liées par 'let', les fonctions, les cas d’union, les constructeurs implicites, les structures et les classes.

Auparavant, le compilateur permettait d'appliquer incorrectement des attributs à des cibles incompatibles. Cela a provoqué des bogues subtils, tels que les attributs de test ignorés lorsque vous avez oublié () d’effectuer une fonction :

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

Dans F# 10, le compilateur applique des cibles d’attributs et déclenche un avertissement lorsque les attributs sont mal appliqués :

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

Remarque de compatibilité importante :

Il s’agit d’un changement de rupture qui peut révéler des problèmes précédemment silencieux dans votre base de code. Les erreurs précoces empêchent les problèmes de détection des tests et garantissent que les attributs tels que les analyseurs et les décorateurs prennent effet comme prévu.

Prise en charge des expressions de tâche avec and!

Vous pouvez désormais attendre plusieurs tâches en parallèle à l’aide de and! dans les expressions de tâche. L’utilisation task est un moyen populaire d’utiliser des flux de travail asynchrones en F#, en particulier lorsque vous avez besoin d’une interopérabilité avec C#. Toutefois, jusqu’à présent, il n’y avait aucun moyen concis d’attendre plusieurs tâches simultanément dans une expression de calcul.

Vous avez peut-être commencé avec du code qui attendait des calculs séquentiellement :

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

Si vous souhaitez ensuite le modifier pour les attendre simultanément, vous utiliseriez Task.WhenAllgénéralement :

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

Dans F# 10, vous pouvez utiliser and! pour une approche plus idiomatique :

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

Cela combine la sémantique de la version simultanée avec la simplicité de la version séquentielle.

Cette fonctionnalité implémente la suggestion de langage F# #1363, et elle est implémentée en tant qu’ajout à la FSharp.Core bibliothèque. La plupart des projets obtiennent automatiquement la dernière version de FSharp.Core à partir du compilateur, sauf s'ils fixent explicitement une version. Dans ce cas, vous devez le mettre à jour pour utiliser cette fonctionnalité.

Amélioration du découpage par défaut

F# 10 supprime une source de friction de longue date avec le découpage des assemblies F#. Le découpage est le processus de suppression du code inutilisé de votre application publiée pour réduire sa taille. Vous n’avez plus besoin de gérer manuellement un ILLink.Substitutions.xml fichier uniquement pour supprimer des objets blob de ressources de métadonnées F# volumineux (signature et données d’optimisation que le compilateur utilise, mais votre application n’a pas besoin au moment de l’exécution).

Lorsque vous publiez avec le trimming activé (PublishTrimmed=true), la compilation F# génère désormais automatiquement un fichier de substitution incorporé qui cible ces ressources F# uniquement pour les outils.

Auparavant, vous deviez gérer manuellement ce fichier pour supprimer les métadonnées. Cela a ajouté la charge de maintenance et a été facile à oublier.

Le résultat est une sortie réduite par défaut, moins de code répétitif à entretenir, et un problème de maintenance en moins. Si vous avez besoin d’un contrôle manuel complet, vous pouvez toujours ajouter votre propre fichier de substitutions. Vous pouvez désactiver la génération automatique avec la <DisableILLinkSubstitutions>false</DisableILLinkSubstitutions> propriété.

Compilation parallèle en préversion

Une mise à jour passionnante pour les utilisateurs F# qui cherchent à réduire les temps de compilation : les fonctionnalités de compilation parallèles se stabilisent. À compter de .NET 10, trois fonctionnalités : la vérification de type basée sur les graphiques, la génération de code IL parallèle et l’optimisation parallèle, sont regroupées sous la propriété du ParallelCompilation projet.

F# 10 active ce paramètre par défaut pour les projets utilisant LangVersion=Preview. Nous prévoyons de l’activer pour tous les projets dans .NET 11.

Veillez à essayer et à voir si cela accélère votre compilation. Pour activer la compilation parallèle en F# 10 :

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

Si vous souhaitez désactiver tout en bénéficiant d’autres fonctionnalités en préversion, définissez la valeur ParallelCompilation false :

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

La compilation parallèle peut réduire considérablement les temps de compilation pour les projets avec plusieurs fichiers et dépendances.

Cache de subsomption de type

Le compilateur met désormais en cache les vérifications de relation de type pour accélérer l’inférence de type et améliorer les performances de l’IDE, en particulier lors de l’utilisation de hiérarchies de types complexes. En stockant et réutilisant les résultats des vérifications de sous-somme précédentes, le compilateur évite les calculs redondants qui ont précédemment ralenti la compilation et IntelliSense.

Gestion du cache :

Dans la plupart des cas, le cache de soussomption de type améliore les performances sans aucune configuration. Toutefois, si vous rencontrez une augmentation de l’empreinte mémoire ou une utilisation accrue du processeur (en raison des workers de maintenance du cache), vous pouvez ajuster le comportement du cache :

  • Pour désactiver entièrement le cache, définissez <LangVersion>9</LangVersion> dans votre fichier projet pour revenir au comportement F# 9.
  • Pour désactiver l’éviction asynchrone du cache (ce qui augmente la pression des threads) et utiliser l’éviction synchrone à la place, définissez la variable d’environnement FSharp_CacheEvictionImmediate=1 .