Nouveautés de F# 9
(learn.microsoft.com)-. F# 9, inclus dans .NET 9, réduit les problèmes de sûreté liés à l’interopérabilité avec C#/.NET grâce aux types référence nullable et à de meilleurs diagnostics du compilateur
- Les propriétés
.Is*sur les unions discriminées, les active patterns partiels renvoyant unboolet les computation expressions vides rendent la syntaxe F# du quotidien plus concise - FSharp.Core ajoute des fonctions aléatoires sur les collections et la prise en charge des expressions de collection C#, ce qui facilite l’usage des collections immuables F# depuis d’autres codes .NET
- Le compilateur signale plus tôt des problèmes comme l’usage incorrect d’attributs, les méthodes IL au-delà de 65 520 éléments ou la visibilité des membres private
- Grâce aux optimisations sur les tests d’égalité, les plages d’entiers et les compréhensions de listes et de tableaux, certaines boucles deviennent 1,25× à 8× plus rapides, et certaines compréhensions de tableaux jusqu’à 10×
F# 9 dans .NET 9
- F# 9 inclut des changements destinés à rendre les programmes plus sûrs, plus robustes et plus performants
- Il est disponible avec .NET 9, et le dernier SDK .NET peut être téléchargé depuis la page de téléchargement de .NET
- Les principaux changements sont développés dans le dépôt open source du code F#
Évolutions du langage
-
Types référence nullable
- F# a été conçu pour éviter
null, maisnullpeut arriver lorsqu’on l’utilise avec des bibliothèques .NET écrites en C# - F# 9 permet d’exprimer de façon sûre au niveau du type des types référence où null est valide, comme
string | null - Un avertissement de nullabilité est émis si l’on assigne
nullà unstringou si l’on accède directement à.Lengthsur une valeur de typestring | null - Si le cas
nullest traité en premier dans un pattern matching, les liaisons suivantes sont ensuite considérées comme non nulles - Dans du code générique, retourner
nullnécessite une contrainte de type référence comme'T : not struct - Plus de détails dans Nullable Reference Types in F# 9
- F# a été conçu pour éviter
-
Propriétés
.Is*sur les unions discriminées- Les unions discriminées disposent de propriétés
.Is*générées automatiquement pour chaque cas - Par exemple, si le type
Contactpossède les casEmailetPhone, on peut vérifier un cas précis avecperson.contact.IsEmail - Auparavant, il fallait écrire un
matchdu typeEmail _ -> true | _ -> falsepour faire la même vérification
- Les unions discriminées disposent de propriétés
-
Retour
boolpour les active patterns partiels- Les active patterns partiels devaient auparavant renvoyer
Some ()en cas de correspondance etNoneen cas d’échec - F# 9 autorise désormais aussi un retour de type
bool - Dans un exemple de correspondance de chaînes insensible à la casse, on peut renvoyer directement le résultat de
String.Equals(..., StringComparison.OrdinalIgnoreCase)
- Les active patterns partiels devaient auparavant renvoyer
-
Priorité aux méthodes d’extension en présence d’arguments
- Certaines bibliothèques .NET définissent des méthodes d’extension portant le même nom qu’une propriété native du type
- F# 9 s’aligne sur ce modèle et, lorsqu’un argument est fourni, résout une méthode d’extension au lieu d’échouer à la vérification de type
- Dans l’exemple, la propriété
XdeFooet la méthode d’extension du même nomX(f: Foo, i: int)peuvent permettre un appel sous la formef.X(1)pour combiner affectation de propriété et chaînage d’appels
-
Computation expressions vides
- F# 9 prend en charge les computation expressions vides
seq { }crée une séquence vide, et des codes comme une DSL HTML peuvent exprimer un bloc vide avecp { }- Une computation expression vide se traduit par un appel à la méthode
Zerodu builder - La syntaxe est plus naturelle que l’ancien
builder { () }
Améliorations des directives hash et de F# Interactive
-
Arguments non chaîne pour les directives hash
- Les directives hash du compilateur n’acceptaient auparavant que des arguments de type chaîne entre guillemets
- En F# 9, elles peuvent recevoir des arguments de type arbitraire
- On peut ainsi écrire
#nowarn 0070au lieu de#nowarn "0070", ou#time onau lieu de#time "on"
-
#helpenrichi dans F# Interactive- La directive
#helpde F# Interactive affiche dans le REPL la documentation d’un objet ou d’une fonction - L’argument peut être fourni sans guillemets
- Par exemple,
#help List.map;;affiche la description, les paramètres, la valeur de retour, des exemples, le nom complet et les informations d’assembly - Plus de détails dans le billet Enhancing #help in F# Interactive blog post
- La directive
-
Préfixe
FSaccepté dans#nowarn- Auparavant, écrire
#nowarn "FS0057"provoquait l’erreurInvalid warning number 'FS0057'même si le numéro d’avertissement était correct - F# 9 accepte désormais les numéros d’avertissement avec le préfixe
FS #nowarn 57,#nowarn 0057,#nowarn FS0057ainsi que les formes chaîne"57","0057","FS0057"fonctionnent toutes- Il est préférable de conserver le même style dans un projet
- Auparavant, écrire
Améliorations de sûreté et de diagnostic du compilateur
-
Avertissement sur les emplacements invalides de
[<TailCall>]- F# 9 émet un avertissement lorsque l’attribut
[<TailCall>]est appliqué à un emplacement inapproprié - Les exemples incluent une fonction non récursive, une valeur liée par
let, ou une valeur liée parletrécursif - Ces attributs n’ont pas d’effet sur le comportement du code, mais peuvent induire en erreur les lecteurs
- F# 9 émet un avertissement lorsque l’attribut
-
Renforcement de l’application de
AttributeTargets- Le compilateur applique désormais correctement
AttributeTargetssur les valeurslet, les fonctions, les déclarations de cas d’union, les constructeurs implicites, lesstructet lesclass - Cela permet d’éviter des bugs discrets, comme dans des tests Xunit lorsqu’on oublie l’argument
unit - Auparavant,
[<Fact>] let ``this test always fails`` = Assert.True(false)n’était pas une vraie fonction, donc le test runner l’ignorait, etdotnet testpassait - Désormais, l’erreur
error FS0842: This attribute is not valid for use on this language elementest émise
- Le compilateur applique désormais correctement
-
Récupération du parseur
- Les améliorations de récupération du parseur permettent aux outils comme la coloration syntaxique de continuer à fonctionner même sur du code incomplet en cours d’édition
- Cela couvre notamment des motifs
asincomplets, des object expressions, des déclarations de casenum, des déclarationsrecord, des patterns complexes de constructeur principal, des identifiants longs non résolus, des clausesmatchvides, ainsi que des champs et types de champs manquants dans des cas d’union
-
Précision des messages et emplacements de diagnostic
- F# 9 ajoute de nouveaux messages de diagnostic et améliore la précision de leur emplacement
- Cela concerne notamment les méthodes
overrideambiguës dans les object expressions, les membres abstraits dans une classe non abstraite, les propriétés ayant le même nom qu’un cas d’union discriminée, les désaccords sur le nombre d’arguments d’un active pattern, les unions avec champs dupliqués, ou l’usage combiné deuse!etand!dans une computation expression - Une nouvelle erreur à la compilation apparaît lorsque le code IL généré comporte une classe avec plus de 65 520 méthodes
- Une telle classe ne peut pas être chargée par le CLR et conduirait à une erreur à l’exécution
-
Option de visibilité réelle
- F# a la particularité d’écrire les membres private en internal dans l’IL, ce qui permettait à des projets non F#, accessibles via
InternalsVisibleTo, d’accéder de façon inappropriée à des membres private de projets F# - F# 9 introduit pour corriger ce comportement un drapeau du compilateur opt-in,
--realsig+ - Dans un
.fsproj, on peut l’activer avec<RealSig>true</RealSig> - Cela permet de vérifier si une solution dépend du comportement précédent
- F# a la particularité d’écrire les membres private en internal dans l’IL, ce qui permettait à des projets non F#, accessibles via
Évolutions de la bibliothèque standard FSharp.Core
-
Fonctions aléatoires sur les collections
- Les modules
List,ArrayetSeqreçoivent des fonctions de mélange et d’échantillonnage aléatoire - Cela facilite l’usage de F# dans des scénarios courants nécessitant de l’aléatoire, comme la data science, le machine learning ou le développement de jeux
- Toutes les fonctions existent en trois variantes
- une variante utilisant une instance partagée de
Random, implicite et thread-safe - une variante recevant une instance de
Randomen argument - une variante recevant une fonction
randomizerpersonnalisée censée renvoyer unfloatsupérieur ou égal à 0.0 et strictement inférieur à 1.0
- une variante utilisant une instance partagée de
- Quatre fonctions sont proposées :
Shuffle,Choice,ChoicesetSample, chacune en trois variantes - La liste complète des fonctions et variantes est disponible dans RFC #1135
- Les modules
-
Comportement des fonctions aléatoires
Shufflerenvoie une nouvelle collection de même type et de même taille, dont les éléments sont mélangés avec une pondération uniforme sur la longueur de la collection- Pour les tableaux, une variante
InPlacemélange directement les éléments dans le tableau existant Choicerenvoie un unique élément aléatoire avec une pondération uniforme sur la taille de la collectionChoicessélectionne N éléments de la collection d’entrée dans un ordre aléatoire, avec possibilité de sélectionner plusieurs fois le même élémentSamplesélectionne N éléments de la collection d’entrée dans un ordre aléatoire, sans jamais choisir deux fois le même élément- Dans
Sample, N ne peut pas être supérieur à la longueur de la collection
-
Constructeur sans argument pour
CustomOperationAttribute- Un constructeur sans argument a été ajouté à
CustomOperationAttribute, ce qui facilite la création d’opérations personnalisées pour les computation expression builders - Dans la plupart des cas, le nom explicite est identique au nom de la méthode ; on peut donc écrire
[<CustomOperation>]au lieu de[<CustomOperation("bar")>]
- Un constructeur sans argument a été ajouté à
-
Prise en charge des expressions de collection C#
- Depuis C#, les listes et ensembles F# peuvent être initialisés avec des expressions de collection
- Par exemple, au lieu de
SetModule.FromArray([1, 2, 3]), on peut écrireFSharpSet<int> mySet = [ 1, 2, 3 ]; - Les collections immuables F# peuvent être utiles lorsqu’on a besoin d’une égalité structurelle absente des collections
System.Collections.Immutable
Améliorations de performances
-
Optimisations des tests d’égalité
- Les tests d’égalité sont plus rapides et provoquent moins d’allocations mémoire
- Dans un exemple de recherche d’une valeur absente avec
Array.containssur un tableau destruct, 1 000 opérations de boxing étaient auparavant effectuées, ce n’est plus le cas - Dans un benchmark de fonctions de tableau sur une
structà 2 membres, le temps moyen deArrayContainsNonexistingpasse de 5 190.95ns à 766.005ns, et les allocations de 24 000B à 0 ArrayTryFindNonexistingpasse de 5 139.58ns à 1 140.515ns, et les allocations de 24 024B à 24B- Plus de détails dans F# Developer Stories: How we’ve finally fixed a 9-year-old performance issue
-
Mutualisation des champs des unions discriminées
struct- Lorsque plusieurs cas d’une union discriminée
structont des champs de même nom et de même type, ils peuvent partager le même emplacement mémoire - Cela réduit l’empreinte mémoire de la
struct - Dans l’exemple, une union discriminée
structpartageant des champs basés sur le mêmeint64occupe 16 octets - Une version précédente utilisant des noms de champs distincts pour chaque cas occupait 60 octets
- Comme les mêmes noms de champs n’étaient pas autorisés auparavant, cela ne pose pas de problème de compatibilité binaire
- Lorsque plusieurs cas d’une union discriminée
-
Optimisation des plages d’entiers
- Le compilateur génère désormais du code optimisé dans davantage de cas pour les expressions
start..finishetstart..step..finish - Auparavant, seules les plages de type
int/int32avec un pas constant de1ou-1étaient optimisées - Les autres types entiers et les autres valeurs de pas utilisaient une implémentation inefficace fondée sur
IEnumerable - Tous ces cas sont désormais optimisés
- Dans
for … in start..finish do …,[start..step..finish]et[for n in start..finish -> f n], les gains vont de 1,25× à 8×
- Le compilateur génère désormais du code optimisé dans davantage de cas pour les expressions
-
Optimisation des compréhensions de listes et de tableaux
- Les compréhensions de listes et de tableaux de la forme
for x in xs -> …sont optimisées - Les améliorations sont particulièrement marquées pour les tableaux
- La vitesse peut être multipliée jusqu’à 10×, et les allocations réduites de moitié à un quart
- Les compréhensions de listes et de tableaux de la forme
Améliorations des outils Visual Studio
-
Activation par défaut des live buffers
- La fonctionnalité live buffers de Visual Studio était auparavant optionnelle, mais elle est désormais activée par défaut après avoir été suffisamment testée
- Le compilateur en arrière-plan qui alimente l’IDE utilise désormais les buffers de fichiers non enregistrés
- Les modifications sont donc prises en compte sans qu’il soit nécessaire d’enregistrer le fichier sur disque
- Auparavant, renommer un symbole présent dans un fichier modifié mais non enregistré pouvait provoquer un comportement inattendu
-
Correctif pour supprimer les parenthèses inutiles
- Visual Studio propose désormais un correctif pour supprimer les parenthèses inutiles
- Par exemple,
let f (x) = xpeut devenirlet f x = x, etlet _ = (2 * 2) + 3peut devenirlet _ = 2 * 2 + 3 - L’objectif est de réduire les parenthèses qui relèvent davantage du bruit que de la clarté
-
Prise en charge des visualiseurs personnalisés dans les projets F#
- Les visualiseurs du débogueur Visual Studio fonctionnent désormais aussi dans les projets F#
-
Info-bulle de signature au milieu d’un pipeline
- Auparavant, l’aide sur les signatures n’était pas fournie lorsqu’une fonction au milieu d’un pipeline avait déjà reçu des paramètres curryfiés complexes
- Désormais, une info-bulle de signature s’affiche pour le paramètre suivant
1 commentaires
Commentaires sur Hacker News
F# est resté mon langage préféré depuis ma première découverte à l’université
Il était très en avance sur C# sur des fonctionnalités comme les discriminated unions, la sécurité vis-à-vis des null, le pattern matching, les records, une inférence de types plus puissante et les contraintes génériques
C’est bien que C# ait fini par adopter ce genre de fonctionnalités avec le temps, mais c’est dommage qu’elles aient été intégrées d’une manière incompatible entre elles
Comme les investissements dans F# sont bien plus faibles que dans C#, il a aussi pris du retard en matière de rythme d’innovation, mais cela reste un excellent langage, globalement compatible avec l’écosystème .NET, et capable d’offrir les mêmes performances que C# avec bien moins de boilerplate
On peut s’en sortir assez facilement en écrivant un projet C# auxiliaire qui contient le « code de glue » nécessaire
Je me demande s’il y a d’autres problèmes précis que vous avez en tête
F# 9 prend aussi en charge l’utilisation d’arguments génériques
ref struct, ajoutée récemment à C#, et si j’ai bien compris il est aussi prévu d’introduire des fonctionnalités définies côté F# lui-mêmeJusqu’ici, ils ont fait un travail impressionnant pour rattraper leur retard, et ça mériterait bien plus de reconnaissance
Le passage disant que « les classes générées en IL avec plus de 65 520 méthodes produisent désormais une nouvelle erreur à la compilation. Ces classes ne peuvent pas être chargées par le CLR et provoqueraient sinon une erreur à l’exécution » est difficile à imaginer
Quoi qu’il en soit, F# est un excellent langage
C’est sans doute la deuxième meilleure chose sortie de Microsoft après Excel, et il rend .NET raisonnable comme plateforme
C’est relativement facile à enseigner à des gens déjà familiers de JS ou TS, et c’est aussi un langage très productif
Il est utilisé dans des contextes variés, des moteurs de jeu aux backends d’entreprise en passant par les applications desktop
Je pense que Microsoft a ralenti sa croissance avec quelques erreurs au début, mais comme langage généraliste c’est vraiment solide et relativement simple à apprendre
Je l’avais essayé rapidement il y a quelques années via LINQPad, un IDE léger, mais je n’ai pas vraiment suivi depuis ses avantages, ses défauts ou son évolution
https://www.linqpad.net/
Chez Phosphor, nous avons pris la grosse décision stratégique de miser l’entreprise et notre orientation technique sur F# pendant plusieurs années
Après plus d’un an d’essais, nous avons finalement réécrit entièrement l’application en TypeScript et Rust
Le produit que nous construisons est un outil de programmation pour utilisateurs finaux, donc la séparation frontend/backend traditionnelle y est floue, et l’écosystème .NET ne s’y prêtait pas très bien
À l’origine, nous voulions utiliser Fable pour compiler le code F# vers JS, Rust, .NET, etc., afin de préserver la sûreté des types entre plusieurs technologies et assurer l’interopérabilité nécessaire
En pratique, l’interopérabilité entre plusieurs bibliothèques s’est révélée bien plus difficile que prévu, et la gestion ainsi que la mise à jour de multiples dépendances et bindings ont été vraiment pénibles
Je pense toujours qu’il est vrai que F# permet d’écrire un code élégant et efficace, mais, du fait de son écosystème et de sa manière d’être conçu, il me semble surtout bien adapté aux applications où la frontière frontend/backend traditionnelle reste claire
Dans ce cas, je n’utiliserais F# que pour le backend
Les technologies qui m’enthousiasment le plus en ce moment sont Effect et Moonbit, que nous utilisons en interne
La bibliothèque Schema d’Effect comble beaucoup de lacunes du système de types de TS, et Moonbit ressemble à une version moderne de F# affranchie de la dépendance à MS/.NET
Moonbit a été conçu par le créateur de ReScript, qu’on peut voir comme un Fable pour OCaml, et il est très bien pensé, avec une compilation directe vers du JS optimisé, du WASM et du natif
Nous utilisons Effect en production mais pas encore Moonbit, néanmoins son potentiel comme langage conçu pour un monde AI-first est assez remarquable
La logique métier centrale et les validateurs étaient écrits en F#, tandis que le reste de l’application frontend était en TypeScript et le backend en C#
Autrement dit, seule la logique cœur et la validation étaient en F#, tandis que toutes les entrées/sorties étaient gérées par TS et C#
C’était un produit similaire, si je me souviens bien écrit en F#, mais je n’ai pas suivi son actualité récemment
Effect a l’air vraiment bien, et j’aimerais qu’il existe aussi pour d’autres langages que TypeScript
MoonBit donne l’impression d’être son propre langage propriétaire, donc j’hésiterais à migrer vers ça plutôt que vers un langage plus connu ; je serais curieux de savoir comment vous voyez cet aspect
J’avais un cours de cryptographie où l’on pouvait choisir n’importe quel langage tournant sur .NET, et un devoir fait en F# était bien plus lisible que ceux des autres
J’aimerais en faire plus souvent, mais pour la data science c’est presque 100 % Python
F# 9 bénéficie lui aussi de presque toutes les améliorations de performances de .NET 9 lui-même
En particulier, les progrès du côté de l’object escape analysis sont importants
https://devblogs.microsoft.com/dotnet/performance-improvemen...
L’époque où je travaillais en F# me manque vraiment
C’est un langage très productif, donc même simplement suivre ses mises à jour reste agréable
Compte tenu de la taille de la communauté et du désintérêt que Microsoft semble parfois lui porter, je trouve que le support outillage était plutôt bon
Le plus gros point de friction pour moi, c’était la précision de la couverture de tests du code
J’ai récemment un peu touché à F#, et en venant de Python, j’aime vraiment le fait de pouvoir expérimenter toutes sortes de choses avec le REPL
Je me demande si les développeurs F# expérimentés l’utilisent aussi de cette façon
Cet hiver, j’aimerais créer un petit projet de backend web pour mieux découvrir le langage et son écosystème
J’ai entendu dire qu’Oxpecker était bien côté HTTP, mais je me demande s’il y a des recommandations pour un client ou un driver PostgreSQL
Je n’aime pas les ORM
https://lanayx.github.io/Oxpecker/
https://monazita.gitlab.io/monazita/ a été créé en apprenant F# pour un projet personnel ; ça fonctionne globalement, mais il y a encore de la marge pour le peaufiner, et c’est spécifique à PostgreSQL
https://github.com/jacentino/DbFun est plus abouti que le projet précédent et prend en charge plusieurs bases de données
Ce serait probablement un bon point de départ
Si une API n’est pas claire, je clique même sur « Send to F# interactive » directement dans la base de code pour exécuter et expérimenter dans le module
Je m’en sers aussi pour tester de nouvelles bibliothèques, faire des benchmarks rapides ou remplacer des scripts PowerShell
Avec le shebang
#!/usr/bin/env -S dotnet fsi, on peut rendre des scripts F# exécutables ; je l’utilise donc souvent comme alternative à bash/Python pour des scripts autour de projets .NET où le dotnet-sdk est déjà installéEn général, c’est plus rapide à exécuter et en gros tout aussi concis
J’ai personnellement l’impression que la syntaxe et les idiomes de F# se prêtent mieux que ceux de C# à la programmation en REPL, où l’on assemble de petits morceaux de code
C# demande en général davantage de structure orientée objet
Je me demande comment fonctionne la gestion des versions de F#
Il y a beaucoup d’améliorations de qualité de vie qui ont l’air intéressantes, mais du point de vue du versionnement sémantique, il ne semble pas y avoir de rupture de compatibilité justifiant un changement de version majeure, et cela ne ressemble pas non plus à un saut de fonctionnalités du langage assez important pour passer de 8 à 9 dans un projet qui n’utilise pas le versionnement sémantique
Un autre commentaire parlait de la sortie récente de .NET 9 ; je me demande si c’est pour aligner la numérotation sur celle de .NET
Je ne sais pas si le passage a été fait volontairement pour coïncider avec la version de .NET, ou si c’est une coïncidence
Par exemple, C# a sorti C# 13 en même temps que .NET 9
dotnet buildCela inclut msbuild, les compilateurs, les packages NuGet, etc.
Par exemple, l’équipe F# aurait pu publier des changements de langage entre 8 et 9, mais même sans cela, elle aurait pu avoir besoin de changements de compilateur ou de msbuild nécessitant .NET 9 pour d’autres raisons
En général, les développeurs peuvent simplement faire évoluer leur code vers la version la plus récente du runtime, sauf s’ils doivent attendre que le dernier .NET soit installé dans leur environnement de déploiement
De nos jours, les changements de MSBuild permettant de créer des déploiements autonomes réduisent aussi ce besoin, mais c’est bon à savoir
Cela dit, C# a 4 versions d’avance
De toute façon, le versionnement sémantique est à mon avis surestimé
Je me demande quel est l’état de F# comme alternative à C# pour créer des applications GUI sous Windows
Je me demande aussi s’il existe des entreprises qui utilisent F# pour ce type de cas
https://github.com/fsprojects/Avalonia.FuncUI
https://fabulous.dev/ cible Avalonia/MAUI/Xamarin
https://github.com/kekyo/epoxy prend en charge Avalonia et WPF
Si tu veux savoir si des entreprises l’utilisent pour ce genre de choses, je peux demander autour de moi
Je n’ai jamais vraiment essayé F# moi-même, mais en parcourant le sujet, cette ressource m’a semblé excellente : https://fsharpforfunandprofit.com/
Il convient aussi bien aux développeurs C# expérimentés qu’aux personnes qui apprennent encore relativement la programmation
Il est peu mis à jour ces temps-ci, donc il est difficile d’y trouver une discussion sur les nouveautés de F# 9, mais les articles existants sont excellents pour comprendre des concepts comme
applyetbind