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.

Voici la présentation des principaux changements apportés par F#9 : types de référence annulables, propriétés d'union discriminées .Is*, la mise à jour de la bibliothèque standard (FSharp.Core). F# 9 apporte également des améliorations de la productivité des développeurs ainsi que les 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. Depuis novembre 2010, Microsoft a mis à la disposition de tous les bibliothèques core et son compilateur F#, sous la licence Apache 22.

F# 9 est disponible et introduit une série d'améliorations pour rendre les programmes plus sûrs, plus résistants et plus performants. F# 9 est disponible dans .NET 9. Voici les principaux changements apportés par F# 9 :

  • Types de référence annulables
  • Propriétés d'union discriminées .Is*
  • Les motifs actifs partiels peuvent renvoyer une option bool au lieu d'une option unit
  • Mises à jour de la bibliothèque standard (FSharp.Core)
  • Amélioration de la productivité des développeurs
  • Amélioration des performances


Types de référence nullables

Bien que F# soit conçu pour éviter null, celles-ci peuvent se glisser dans les interfaces avec les bibliothèques .NET écrites en C#. F# fournit désormais un moyen sûr de traiter les types de référence qui peuvent avoir null comme valeur valide.

Voici quelques exemples :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Declared type at let-binding
let notAValue: string | null = null
 
let isAValue: string | null = "hello world"
 
let isNotAValue2: string = null // gives a nullability warning
 
let getLength (x: string | null) = x.Length // gives a nullability warning since x is a nullable string
 
// Parameter to a function
let len (str: string | null) =
    match str with
    | null -> -1
    | NonNull s -> s.Length  // binds a non-null result
 
// Parameter to a function
let len (str: string | null) =
    let s = nullArgCheck "str" str // Returns a non-null string
    s.Length  // binds a non-null result
 
// Declared type at let-binding
let maybeAValue: string | null = hopefullyGetAString()
 
// Array type signature
let f (arr: (string | null)[]) = ()
 
// Generic code, note 'T must be constrained to be a reference type
let findOrNull (index: int) (list: 'T list) : 'T | null when 'T : not struct =
    match List.tryItem index list with
    | Some item -> item
    | None -> null


Propriétés .Is* de l'union discriminée

Les unions discriminées ont désormais des propriétés auto-générées pour chaque cas, ce qui permet de vérifier si une valeur appartient à un cas particulier. Par exemple, pour le type suivant :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
type Contact =
    | Email of address: string
    | Phone of countryCode: int * number: string
 
type Person = { name: string; contact: Contact }
Auparavant, vous deviez écrire quelque chose comme :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
let canSendEmailTo person =
    match person.contact with
    | Email _ -> true
    | _ -> false
Maintenant, vous pouvez écrire :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
let canSendEmailTo person =
    person.contact.IsEmail


Les motifs actifs partiels peuvent renvoyer bool au lieu de unit option

Auparavant, les motifs actifs partiels renvoyaient Some () pour indiquer une correspondance et None dans le cas contraire. Désormais, ils peuvent également renvoyer bool.

Par exemple, le motif actif pour ce qui suit :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...
s'écrivait auparavant comme suit:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
        Some ()
    else
        None
Désormais, vous pouvez écrire: :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)


Mises à jour de la bibliothèque standard (FSharp.Core)

Fonctions aléatoires pour les collections

Les modules List, Array et Seq disposent de nouvelles fonctions d'échantillonnage et de mélange aléatoires. Cela facilite l'utilisation de F# pour la science des données, l'apprentissage automatique, le développement de jeux et d'autres scénarios où l'aléatoire est nécessaire.

Toutes les fonctions ont les variantes suivantes :

  • Une qui utilise une instance Random implicite, partagée et à l'abri des threads
  • Une fonction qui prend une instance Random comme argument
  • Une qui prend une fonction de randomizer personnalisée, qui doit renvoyer une valeur flottante supérieure ou égale à 0,0 et inférieure à 1,0.


Quatre fonctions (chacune avec trois variantes) sont disponibles : Shuffle, Choice, Choices et Sample.

  • Shuffle

    Les fonctions Shuffle renvoient une nouvelle collection de même type et de même taille, chaque élément étant placé dans une position aléatoirement mélangée. La probabilité de se retrouver dans n'importe quelle position est pondérée uniformément en fonction de la longueur de la collection.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    let allPlayers = [ "Alice"; "Bob"; "Charlie"; "Dave" ]
    let round1Order = allPlayers |> List.randomShuffle // [ "Charlie"; "Dave"; "Alice"; "Bob" ]
    Pour les tableaux, il existe également des variantes InPlace qui mélangent les éléments du tableau existant au lieu d'en créer un nouveau.

  • Choice

    Les fonctions Choice renvoient un seul élément aléatoire de la collection donnée. Le choix aléatoire est pondéré en fonction de la taille de la collection.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    let allPlayers = [ "Alice"; "Bob"; "Charlie"; "Dave" ]
    let randomPlayer = allPlayers |> List.randomChoice // "Charlie"

  • Choices

    Les fonctions Choices sélectionnent N éléments de la collection d'entrée dans un ordre aléatoire, ce qui permet de sélectionner des éléments plus d'une fois.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    let weather = [ "Raining"; "Sunny"; "Snowing"; "Windy" ]
    let forecastForNext3Days = weather |> List.randomChoices 3 // [ "Windy"; "Snowing"; "Windy" ]

  • Sample

    Les fonctions Sample sélectionnent N éléments de la collection d'entrée dans un ordre aléatoire, sans permettre aux éléments d'être sélectionnés plus d'une fois. N ne peut être supérieur à la longueur de la collection.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    let foods = [ "Apple"; "Banana"; "Carrot"; "Donut"; "Egg" ]
    let today'sMenu = foods |> List.randomSample 3 // [ "Donut"; "Apple"; "Egg" ]



Amélioration de la productivité des développeurs

  • Récupération de l'analyseur syntaxique

    Des améliorations constantes ont été apportées à la récupération de l'analyseur syntaxique, ce qui signifie que les outils (par exemple, la coloration syntaxique) continuent de fonctionner avec le code lorsque vous êtes en train de l'éditer et qu'il peut ne pas être syntaxiquement correct à tout moment.

    Par exemple, l'analyseur syntaxique récupère désormais les motifs as non terminés, les expressions d'objet, les déclarations de cas enum, les déclarations d'enregistrement, les motifs de constructeur primaire complexes, les identificateurs longs non résolus, les clauses de correspondance vides, les champs de cas d'union manquants et les types de champs de cas d'union manquants.

  • Diagnostics

    Les diagnostics, ou la compréhension de ce que le compilateur n'aime pas dans votre code, sont une partie importante de l'expérience utilisateur avec F#. Il y a un certain nombre de messages de diagnostic nouveaux ou améliorés ou des emplacements de diagnostic plus précis dans F# 9.

    En voici quelques-uns :

    • Méthode de remplacement ambiguë dans une expression d'objet
    • Membres abstraits utilisés dans des classes non abstraites
    • Propriété ayant le même nom qu'un cas d'union discriminée
    • Inadéquation du nombre d'arguments d'un motif actif
    • Unions avec champs dupliqués
    • Utilisation de use ! avec and ! dans les expressions de calcul

    Il existe également une nouvelle erreur à la compilation pour les classes comportant plus de 65 520 méthodes dans l'IL générée. Ces classes ne sont pas chargeables par le CLR et entraînent une erreur d'exécution. (Vous n'autoriserez pas autant de méthodes, mais il y a eu des cas avec du code généré).

  • Visibilité réelle

    Il existe une bizarrerie dans la manière dont F# génère les assemblages qui fait que les membres privés sont écrits dans IL comme étant internes. Cela permet un accès inapproprié aux membres privés à partir de projets non F# qui ont accès à un projet F# via InternalsVisibleTo.

    Il existe maintenant un correctif optionnel pour ce comportement disponible via l'option --realsig+ du compilateur. Essayez-le dans votre solution pour voir si l'un de vos projets dépend de ce comportement. Vous pouvez l'ajouter à vos fichiers .fsproj comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <PropertyGroup>
        <RealSig>true</RealSig>
    </PropertyGroup>



Amélioration des performances

Optimisation des vérifications d'égalité

Les vérifications d'égalité sont désormais plus rapides et allouent moins de mémoire.

Par exemple :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
[<Struct>]
type MyId =
    val Id: int
    new id = { Id = id }
 
let ids = Array.init 1000 MyId
let missingId = MyId -1
 
// used to box 1000 times, doesn't box anymore
let _ = ids |> Array.contains missingId


Résultats de l'évaluation comparative des fonctions de tableau affectées, appliquées à une structure à 2 membres

Avant :

Nom : 1.jpg
Affichages : 4795
Taille : 57,8 Ko

Après :

Nom : 2.jpg
Affichages : 931
Taille : 54,2 Ko

Source : Présentation de F# 9

Et vous ?

Pensez-vous que ces améliorations sont crédibles ou pertinentes ?
Quel est votre avis sur le sujet ?

Voir aussi :

La version 6 de F#, le langage de programmation fonctionnel conçu par Microsoft, est disponible, plus rapide et plus interopérable, elle apporte de nouvelles fonctions

Annonces et mises à jour de .NET à la Microsoft Build 2024 : intégration profonde de l'IA, simplification du développement-natif avec .Net Aspire, ainsi que des améliorations des fonctionnalités C# pour C# 13

Microsoft partage sa vision initiale de .NET 9 et publie le premier aperçu de la nouvelle version du framework, dont les domaines d'intérêt sont le développement d'apps cloud-natives et intelligentes