Microsoft annonce que le langage de programmation F# 10 est disponible avec .NET 10 et Visual Studio 2026, apportant des améliorations axées sur la clarté, la cohérence et les performances
F# 10 est désormais disponible avec .NET 10 et Visual Studio 2026. Cette version est une version améliorée axée sur la clarté, la cohérence et les performances, apportant des améliorations modestes mais significatives qui rendent votre code quotidien plus lisible et plus robuste. Avec F# 10, Microsoft poursuit sa démarche visant à simplifier le langage et à améliorer ses performances.
F# est un langage de programmation fonctionnel, impératif et orienté objet pour la plate-forme .NET. F# est développé par Microsoft Research et son noyau est dérivé du langage OCaml, avec lequel il est fortement compatible. Ces deux langages de programmation font partie de la même famille que les langages ML. Ce langage a été conçu spécifiquement pour la plate-forme .NET, donc fortement orienté-objet. Depuis novembre 2010, Microsoft a mis à la disposition de tous les bibliothèques core et son compilateur F#, sous la licence Apache 2.
Récemment, Microsoft a annoncé la dernière version de son framework logiciel .NET 10, apportant des améliorations au compilateur JIT, des améliorations NativeAOT, ainsi que l'optimisation du runtime. Il s'agit d'une version à support à long terme (LTS) qui devrait recevoir des mises à jour jusqu'en novembre 2028. Selon Microsoft, .NET 10 est la version la plus productive, moderne, sécurisée, intelligente et performante de .NET à ce jour.
F# 10 est désormais disponible avec .NET 10 et Visual Studio 2026. Cette version est une version améliorée axée sur la clarté, la cohérence et les performances, apportant des améliorations modestes mais significatives qui rendent votre code quotidien plus lisible et plus robuste.
Avec F# 10, Microsoft poursuit sa démarche visant à simplifier le langage et à améliorer ses performances. Les principales améliorations ergonomiques comprennent la suppression des avertissements dans un certain périmètre, une syntaxe plus cohérente pour les expressions de calcul et une meilleure prise en charge des accesseurs de propriétés automatiques. Cette version comprend également une mise à niveau de l'infrastructure afin d'accélérer la compilation et les outils interactifs sous la forme d'un nouveau cache de subsomption de types.
Améliorations du langage
1. Suppression des avertissements dans une portée donnée
La première fonctionnalité que je souhaite présenter pour F# 10 est très demandée : la possibilité de supprimer les avertissements dans des sections de code arbitraires. Le compilateur prend désormais en charge la directive #warnon, qui s'associe à #nowarn pour activer ou désactiver les avertissements dans une plage de code spécifique.
Un exemple :
1 2 3
| // We know f is never called with a None.
let f (Some a) = // creates warning 25, which we want to suppress
// 2000 loc, where the incomplete match warning is beneficial |
Il est possible d'ajouter une directive #nowarn 25 juste au-dessus de la définition de la fonction, mais sans une directive #warnon 25 correspondante, cela désactivera l'avertissement FS0025 dans le reste du fichier, risquant ainsi de supprimer des problèmes légitimes ailleurs.
Avec F# 10, vous pouvez délimiter explicitement la section, en appliquant la suppression des avertissements à une portée aussi étroite qu'une seule ligne :
1 2 3 4
| #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 du compilateur), vous pouvez l'activer localement avec #warnon. Cette directive s'appliquera alors jusqu'à ce qu'une directive #nowarn correspondante soit rencontrée ou jusqu'à la fin du fichier.
Compatibilité :
Cette fonctionnalité s'accompagne de plusieurs modifications qui améliorent la cohérence des directives #nowarn/#warnon, ce qui se traduit par un comportement plus prévisible. Cependant, il s'agit de modifications importantes qui pourraient affecter votre base de code lorsque vous passerez à F# 10. Voici quelques exemples parmi les plus susceptibles de se produire :
- Les directives d'avertissement multilignes et vides ne sont plus autorisées.
- Les espaces entre # et nowarn ne sont plus autorisés.
- Les chaînes à triple guillemet, interpolées ou littérales ne peuvent pas être utilisées pour les numéros d'avertissement.
Le comportement des scripts est également affecté. Dans les versions précédentes, une directive #nowarn placée n'importe où dans un script s'appliquait à l'ensemble de la compilation. Désormais, son comportement dans les scripts correspond à celui des fichiers .fs, s'appliquant uniquement jusqu'à la fin du fichier ou jusqu'à une directive #warnon correspondante.
2. Modificateurs d'accès sur les accesseurs de propriétés automatiques
Un modèle fréquent dans la programmation orientée objet consiste à créer un état lisible publiquement mais modifiable en privé. Avant F# 10, pour y parvenir, il fallait utiliser une syntaxe de propriété explicite avec des champs de support, ce qui ajoutait beaucoup de code standard :
1 2 3
| 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 appliquer des modificateurs d'accès distincts à des accesseurs de propriétés individuels. Cela vous permet de spécifier différents niveaux d'accès pour le getter et le setter d'une propriété en ligne, ce qui permet d'utiliser des modèles courants tels que des états lisibles publiquement mais modifiables en privé sans code standard verbeux.
Vous pouvez désormais réécrire ce qui précède comme suit :
1 2
| type Ledger() =
member val Balance = 0m with public get, private set |
À noter :
Vous pouvez placer un modificateur d'accès soit avant le nom de la propriété (s'appliquant aux deux accesseurs), soit avant les accesseurs individuels, mais pas les deux simultanément.
De plus, cette fonctionnalité ne s'étend pas aux fichiers de signature (.fsi). La signature correcte pour l'exemple Ledger ci-dessus serait :
1 2 3
| type Ledger() =
member Balance : decimal
member private Balance : decimal with set |
3. Paramètres facultatifs ValueOption
Les paramètres facultatifs peuvent désormais utiliser une représentation ValueOption<'T> basée sur une structure. En appliquant l'attribut [<Struct>] à un paramètre facultatif, vous pouvez demander au compilateur d'utiliser ValueOption<'T> au lieu du type option basé sur une référence. Cela évite une allocation de mémoire pour le wrapper d'option, ce qui est avantageux dans le code où les performances sont critiques.
Les versions précédentes de F# utilisaient toujours le type option alloué dans le tas pour les paramètres facultatifs, même lorsque le paramètre était absent. Dans les scénarios à haut débit ou la création d'objets en boucle interne, cela imposait une pression inutile sur le GC. Les développeurs travaillant sur du code sensible aux performances n'avaient aucun moyen d'éviter ces allocations :
1 2 3 4 5 6
| // 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" |
Vous pouvez désormais utiliser l'attribut [<Struct>] sur un paramètre facultatif pour tirer parti de ValueOption basé sur une structure et éliminer les allocations lorsque l'argument est absent :
1 2 3 4 5
| type X() =
static member M([<Struct>] ?x : string) =
match x with
| ValueSome v -> printfn "ValueSome %s" v
| ValueNone -> printfn "ValueNone" |
Choisissez cette option basée sur une structure pour les petites valeurs ou les types fréquemment construits où la pression d'allocation est importante. Utilisez option par défaut basée sur les références lorsque vous vous appuyez sur des aides de correspondance de modèles existantes, 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 du langage F# qui prennent déjà en charge ValueOption.
4. Prise en charge des appels en queue dans les expressions de calcul
Les constructeurs d'expressions de calcul (par exemple, les coroutines ou autres constructeurs implémentés avec des machines à états reprenables) peuvent désormais opter pour des optimisations d'appels en queue. Lors du désucrage, le compilateur reconnaît lorsqu'une expression telle que return!, yield! ou do! apparaît en position de queue et, lorsque le constructeur fournit des méthodes spéciales, achemine ces appels vers les points d'entrée optimisés.
Si un générateur implémente :
- ReturnFromFinal, le compilateur l'appellera pour un return! en queue (en revenant à ReturnFrom si la variante finale est absente).
- YieldFromFinal, le compilateur l'appellera pour un yield! en queue (en revenant à YieldFrom s'il est absent).
- Pour un do! terminal, le compilateur préférera ReturnFromFinal, puis YieldFromFinal, avant de revenir à la voie Bind normale.
Ces membres *Final sont facultatifs et existent uniquement pour permettre l'optimisation : ils permettent aux constructeurs de court-circuiter les continuations ou de renoncer prématurément aux ressources. Les constructeurs qui ne fournissent pas ces membres conservent leur sémantique existante inchangée.
Exemples :
1 2 3
| coroutine {
yield! subRoutine() // tail position -> YieldFromFinal if available
} |
1 2 3 4 5
| coroutine {
try
yield! subRoutine() // not tail -> normal YieldFrom
finally ()
} |
Compatibilité :
Cette modification peut être préjudiciable si un constructeur définit déjà des membres avec ces noms. Dans la plupart des cas, elle est rétrocompatible : les constructeurs existants continuent de fonctionner sans modification lorsqu'ils sont compilés avec F# 10. Les anciens compilateurs ignoreront simplement les nouvelles méthodes *Final, de sorte que les constructeurs qui doivent rester compatibles avec les versions antérieures du compilateur ne doivent pas supposer que ces méthodes seront invoquées.
5. Liaisons typées dans les expressions de calcul sans parenthèses
Une incohérence de longue date dans la syntaxe des annotations de type pour les liaisons d'expressions de calcul a été résolue. Vous pouvez désormais ajouter des annotations de type aux liaisons let!, use! et and! sans mettre l'identifiant entre parenthèses.
Les versions précédentes de F# exigeaient des parenthèses pour les annotations de type dans les liaisons d'expressions de calcul. Par exemple, let! (x: int) = fetchA() était valide, mais la forme plus naturelle let! x: int = fetchA() provoquait une erreur. Cela obligeait les développeurs à utiliser des parenthèses visuellement encombrantes, même pour des annotations de type simples :
1 2 3 4 5 6
| async {
let! (a: int) = fetchA()
and! (b: int) = fetchB()
use! (d: MyDisposable) = acquireAsync()
return a + b
} |
Vous pouvez désormais écrire des annotations de type sans parenthèses, conformément au style des liaisons let ordinaires :
1 2 3 4 5 6
| async {
let! a: int = fetchA()
and! b: int = fetchB()
use! d: MyDisposable = acquireAsync()
return a + b
} |
6. Autoriser _ dans les liaisons use!
Les versions précédentes de F# rejetaient le modèle de suppression dans les liaisons use!, même lorsque la valeur de la ressource elle-même n'était jamais référencée. Cette incohérence avec les liaisons use ordinaires obligeait les développeurs à créer des identifiants jetables tels que __ ou _ignored uniquement pour satisfaire le compilateur.
Le modèle de suppression (_) fonctionne désormais dans les liaisons use! au sein des expressions de calcul. F# 10 vous permet d'utiliser _ directement lors de la liaison de ressources asynchrones dont les valeurs ne sont nécessaires que pour la gestion de la durée de vie, sans être obligé de fournir un identifiant nommé.
Vous pouvez désormais utiliser directement le modèle discard, ce qui clarifie votre intention et correspond au comportement de use :
1 2 3 4
| counterDisposable {
use! _ = new Disposable()
// logic
} |
7. Rejet des modules pseudo-imbriqués dans les types
La validation structurelle a été renforcée dans cette version afin de rejeter le placement trompeur de modules dans les types. F# 10 génère désormais une erreur lorsqu'une déclaration module apparaît indentée au même niveau structurel dans une définition de type, ce qui évite une source courante de confusion concernant la portée des modules.
Les versions précédentes de F# acceptaient les déclarations module indentées dans les définitions de types, mais ces modules étaient en fait créés comme des éléments frères du type plutôt que d'être imbriqués dans celui-ci. Ce modèle d'indentation induisait souvent les développeurs en erreur en leur faisant croire qu'ils avaient créé un module imbriqué, ce qui entraînait un comportement de portée inattendu :
1 2 3 4 5
| type U =
| A
| B
module M = // Silently created a sibling module, not nested
let f () = () |
Désormais, ce modèle génère l'erreur FS0058, vous obligeant à clarifier votre intention avec un placement correct des modules :
1 2 3 4 5 6
| type U =
| A
| B
module M =
let f () = () |
8. Avertissement de dépréciation pour seq omis
Un avertissement de dépréciation apparaît désormais pour les expressions de séquence nues qui omettent le constructeur seq. F# 10 vous avertit lorsque vous utilisez des accolades de plage nues telles que { 1..10 } ou des formes similaires, vous encourageant à utiliser la forme explicite seq { ... } pour plus de cohérence avec le modèle d'expression de calcul plus large.
Historiquement, F# autorisait une syntaxe spéciale « sequence comprehension lite » dans laquelle le mot-clé seq pouvait être omis. Cela divergeait du fonctionnement des autres expressions de calcul et créait une incohérence dans le langage :
{ 1..10 } |> List.ofSeq // implicit sequence
Désormais, le compilateur signale ce modèle et encourage l'utilisation de la forme explicite qui clarifie la sémantique :
seq { 1..10 } |> List.ofSeq
Il s'agit actuellement d'un avertissement, et non d'une erreur, ce qui vous laisse le temps de mettre à jour votre base de code. La forme explicite seq améliore la clarté du code et sa cohérence avec d'autres expressions de calcul. Les versions futures de F# pourraient en faire une erreur, nous vous recommandons donc d'adopter la syntaxe explicite lors de la mise à jour du code.
9. Application des cibles d'attributs
La validation des cibles d'attributs sera appliquée dans cette version à toutes les constructions du langage. F# 10 vérifie que les attributs ne sont appliqués qu'à leurs cibles prévues en vérifiant les AttributeTargets dans les valeurs liées à let, les fonctions, les cas d'union, les constructeurs implicites, les structures et les classes.
Les versions précédentes de F# autorisaient silencieusement l'application incorrecte d'attributs à des cibles incompatibles. Cela provoquait des bogues subtils, tels que l'ignorance des attributs de test lorsque vous oubliez () pour créer une fonction ou l'inefficacité des directives d'analyseur, ce qui entraînait des divergences CI déroutantes :
1 2 3
| [<Fact>]
let ``this is not a function`` = // Silently ignored, not a test!
Assert.True(false) |
Désormais, le compilateur applique les cibles d'attributs et génère un avertissement lorsque les attributs sont mal appliqués :
1 2 3 4
| [<Fact>]
//^^^^ - warning FS0842: This attribute cannot be applied to property, field, return value. Valid targets are: method
let ``works correctly`` =
Assert.True(true) |
Compatibilité :
Il s'agit d'un changement radical qui peut révéler des problèmes auparavant silencieux dans votre base de code. Les erreurs précoces empêchent les problèmes de découverte de tests et garantissent que les attributs tels que les analyseurs et les décorateurs prennent effet comme prévu.
Amélioration de FSharp.Core – prise en charge de and! dans les expressions de tâche
Cette version apporte une seule amélioration à la bibliothèque FSharp.Core : la prise en charge de and! dans l'expression de calcul task.
L'utilisation de task est un moyen courant de travailler avec des flux de travail asynchrones dans F#, en particulier lorsque l'interopérabilité avec C# est requise. Cependant, jusqu'à récemment, il n'existait aucun moyen concis d'attendre plusieurs tâches simultanément dans une expression de calcul.
Vous avez peut-être commencé avec un code qui attendait les calculs de manière séquentielle :
1 2 3 4 5 6
| // Awaiting sequentially
task {
let! a = fetchA()
let! b = fetchB()
return combineAB a b
} |
Si vous vouliez ensuite le modifier pour les attendre simultanément, vous utilisiez généralement Task.WhenAll :
1 2 3 4 5 6 7
| // 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
} |
Avec F# 10, vous pouvez à la place écrire une version plus idiomatique en utilisant and! :
1 2 3 4 5
| task {
let! a = fetchA()
and! b = fetchB()
return combineAB a b
} |
Cela combine la sémantique du deuxième extrait avec la simplicité du premier.
Performances et outils
Cache de subsomption de types
Cette version introduit également un nouveau cache de subsomption de types afin d'accélérer la vérification des types et d'améliorer la réactivité de l'EDI, en particulier dans les projets comportant des hiérarchies de types complexes. Dans F# 10, le compilateur mémorise les résultats des vérifications de relations de types, ce qui réduit les calculs redondants et améliore les performances globales du compilateur et des outils.
Auparavant, le compilateur F# effectuait de manière répétée des vérifications de subsomption coûteuses lorsqu'il traitait des hiérarchies de types volumineuses, telles que celles impliquant de nombreuses primitives numériques ou de nombreuses implémentations d'interface. Cela pouvait entraîner une latence notable lors de l'inférence de types et des opérations de l'IDE, en particulier dans les solutions volumineuses ou lors de longues sessions d'édition, consommant ainsi davantage de CPU et de mémoire.
Désormais, le nouveau cache de subsomption de types stocke les résultats de ces vérifications. Lorsque le compilateur doit déterminer si un type peut être utilisé à la place d'un autre, il consulte d'abord le cache. Cette mémorisation évite de recalculer les mêmes relations de types, ce qui accélère la compilation et rend IntelliSense plus réactif.
Aucune modification du code n'est nécessaire pour bénéficier de cette amélioration ; les gains de performances sont automatiques.
Meilleur rognage par défaut
F# 10 supprime un problème de longue date lié au rognage des assemblages F# : vous n'avez plus besoin de gérer manuellement un fichier ILLink.Substitutions.xml uniquement pour supprimer les gros blobs de ressources de métadonnées F# (données de signature/optimisation) qui ne sont pas nécessaires lors de l'exécution dans l'application finale.
Lorsque vous publiez avec le rognage activé (PublishTrimmed=true), la compilation F# génère désormais automatiquement un fichier de substitutions qui cible les ressources F# réservées aux outils. Résultat : une sortie plus petite par défaut, moins de code standard et un risque de maintenance en moins.
Si vous avez besoin d'un contrôle manuel complet, vous pouvez toujours ajouter votre propre fichier de substitutions. La génération automatique peut être désactivée à l'aide d'une propriété, comme suit : <DisableILLinkSubstitutions>false</DisableILLinkSubstitutions>.
Compilation parallèle en préversion
Une mise à jour intéressante pour les utilisateurs de F# qui cherchent à réduire les temps de compilation de leurs projets est la stabilisation progressive des fonctionnalités de compilation parallèle. À partir de .NET 10, trois fonctionnalités : la vérification des types basée sur les graphes, la génération parallèle de code IL et l'optimisation parallèle sont regroupées sous la propriété de projet ParallelCompilation.
Actuellement, ce paramètre est activé par défaut pour les projets qui utilisent LangVersion=Preview, et ils prévoient de l'activer pour tout le monde dans .NET 11. Toutefois, si vous souhaitez désactiver cette fonctionnalité tout en continuant à profiter des autres fonctionnalités de prévisualisation, définissez ParallelCompilation sur false.
Mode de vérification de type uniquement pour les scripts
F# est un excellent langage de script, et nous voulons nous assurer que les outils prennent également en charge ce cas d'utilisation. C'est pourquoi ils ont étendu l'indicateur de compilation --typecheck-only afin qu'il fonctionne également pour les scripts .fsx, où il est sans doute le plus utile. Dans F# 9 et les versions antérieures, il n'existait aucun moyen simple de valider la syntaxe et l'exactitude des types d'un script sans exécuter le code, ce qui produisait souvent des effets secondaires.
Avec cette version, vous pouvez simplement utiliser l'indicateur --typecheck-only lors de l'appel de fsi pour vérifier le type du script sans l'exécuter. Cette fonctionnalité facilite l'ajout de portes CI qui permettront de détecter la détérioration des scripts dans votre base de code.
Avertissement concernant un problème connu : il existe un bug connu qui limite considérablement les avantages de l'utilisation de --typecheck-only avec des scripts qui en incluent d'autres via #load. Dans ce cas, la vérification des types se terminera prématurément après le traitement du premier fichier source chargé. Cependant, le correctif a déjà été implémenté et sera inclus dans la prochaine version 10.0.200.
Conclusion
F# est développé dans le cadre d'une collaboration entre la Fondation .NET, la Fondation F# Software, leurs membres et d'autres contributeurs, dont Microsoft. La communauté F# est impliquée à toutes les étapes de l'innovation, de la conception, de la mise en œuvre et de la livraison. Le travail sur F# 11 est déjà en cours : amélioration des performances, amélioration du langage et mise à niveau des outils.
Source : Présentation de F# 10
Et vous ?
Pensez-vous que cette présentation est crédible ou pertinente ?
Quel est votre avis sur le sujet ?
Voir aussi :
F# 9 est disponible avec des améliorations pour rendre les programmes plus sûrs, plus résistants et plus performants, notamment les types de référence annulables et la mise à jour de la bibliothèque standard
Microsot annonce que Visual Studio 2026 est disponible : l'EDI apporte des améliorations en termes de performances, une expérience utilisateur repensée et une avancée dans le développement basé sur l'IA
Microsoft présente les nouvelles fonctionnalités de C# 14 qui devraient permettre aux développeurs C# de bénéficier de certaines des améliorations de performances offertes par .NET 10
Partager