Nouveautés de F# 6

F# 6 apporte plusieurs améliorations au langage F# et à F# Interactive. Il est publié avec .NET 6.

Vous pouvez télécharger la dernière version du kit SDK .NET sur la page de téléchargements .NET.

Bien démarrer

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

task {…}

F# 6 inclut la prise en charge native de la création de tâches .NET dans le code F#. Par exemple, examinons le code F# suivant pour la création d’une tâche compatible avec .NET :

let readFilesTask (path1, path2) =
   async {
        let! bytes1 = File.ReadAllBytesAsync(path1) |> Async.AwaitTask
        let! bytes2 = File.ReadAllBytesAsync(path2) |> Async.AwaitTask
        return Array.append bytes1 bytes2
   } |> Async.StartAsTask

À l’aide de F# 6, ce code peut être réécrit comme suit.

let readFilesTask (path1, path2) =
   task {
        let! bytes1 = File.ReadAllBytesAsync(path1)
        let! bytes2 = File.ReadAllBytesAsync(path2)
        return Array.append bytes1 bytes2
   }

La prise en charge des tâches était disponible avec F# 5 grâce aux excellentes bibliothèques TaskBuilder.fs et Ply. La migration du code vers la prise en charge intégrée ne présente en théorie aucune difficulté. Il existe néanmoins quelques différences : les espaces de noms et l’inférence de type diffèrent légèrement entre la prise en charge intégrée et ces bibliothèques, et des annotations de type supplémentaires peuvent être nécessaires. Si nécessaire, vous pouvez toujours utiliser ces bibliothèques communautaires avec F# 6 si vous les référencez explicitement et ouvrez les espaces de noms appropriés dans chaque fichier.

L’utilisation de task {…} est très semblable à l’utilisation de async {…}. L’utilisation de task {…} présente plusieurs avantages par rapport à async {…} :

  • La surcharge de task {...} est plus réduite, ce qui permet éventuellement d’améliorer les performances des chemins de code critiques, où le travail asynchrone s’exécute rapidement.
  • Le débogage des exécutions pas à pas et des traces de pile est préférable pour task {…}.
  • Il est plus facile d’interagir avec des packages .NET qui attendent ou produisent des tâches.

Si vous maîtrisez async {…}, il y a quelques différences à connaître :

  • task {…} exécute immédiatement la tâche jusqu’au premier point d’attente.
  • task {…} ne propage pas implicitement un jeton d’annulation.
  • task {…} n’effectue pas de vérification d’annulation implicite.
  • task {…} ne prend pas en charge les « tailcalls » asynchrones. Cela signifie que l’utilisation récursive de return! .. peut entraîner des dépassements de la capacité de la pile s’il n’y a pas d’attente asynchrone intermédiaire.

En règle générale, il est préférable d’utiliser task {…} plutôt que async {…} dans le nouveau code si vous interagissez avec des bibliothèques .NET qui utilisent des tâches, et si vous ne comptez pas sur des « tailcalls » de code asynchrones ou sur la propagation implicite de jetons d’annulation. Dans le code existant, vous ne devez passer à task {…} qu’après avoir examiné votre code pour vous assurer que vous ne vous appuyez pas sur les caractéristiques de async {…} mentionnées précédemment.

Cette fonctionnalité implémente F# RFC FS-1097.

Syntaxe d’indexation plus simple avec expr[idx]

F# 6 permet d’utiliser la syntaxe expr[idx] pour indexer et découper les collections.

Jusqu’à F# 5 inclus, F# utilisait expr.[idx] comme syntaxe d’indexation. L’autorisation de l’utilisation de expr[idx] repose sur les commentaires répétés des personnes qui apprennent à utiliser le langage F# ou qui le découvrent pour la première fois, et selon lesquelles l’utilisation de l’indexation de la notation par points est perçue comme une divergence inutile par rapport aux pratiques courantes du secteur.

Il ne s’agit pas d’un changement cassant, car par défaut, aucun avertissement n’est émis lors de l’utilisation de expr.[idx]. Toutefois, certains messages d’information suggérant des clarifications de code sont émis. Vous pouvez également activer d’autres messages d’information. Par exemple, vous pouvez activer un avertissement informatif facultatif (/warnon:3566) pour commencer à signaler les utilisations de la notation expr.[idx]. Pour plus d’informations, consultez Notation de l’indexeur.

Dans le nouveau code, nous recommandons l’utilisation systématique de expr[idx] comme syntaxe d’indexation.

Cette fonctionnalité implémente F# RFC FS-1110.

Représentations de struct pour les modèles actifs partiels

F# 6 ajoute à la fonctionnalité des « motifs actifs » des représentations de struct facultatives pour les modèles actifs partiels. Cela vous permet d’utiliser un attribut pour contraindre un modèle actif partiel à renvoyer une option de valeur :

[<return: Struct>]
let (|Int|_|) str =
   match System.Int32.TryParse(str) with
   | true, int -> ValueSome(int)
   | _ -> ValueNone

L’utilisation de l’attribut est obligatoire. Sur les sites d’utilisation, le code ne change pas. Le résultat net est que les allocations sont réduites.

Cette fonctionnalité implémente F# RFC FS-1039.

Opérations personnalisées surchargées dans les expressions de calcul

F# 6 vous permet d’utiliser CustomOperationAttribute sur les méthodes surchargées.

Examinons l’utilisation suivante d’un générateur d’expressions de calcul content :

let mem = new System.IO.MemoryStream("Stream"B)
let content = ContentBuilder()
let ceResult =
    content {
        body "Name"
        body (ArraySegment<_>("Email"B, 0, 5))
        body "Password"B 2 4
        body "BYTES"B
        body mem
        body "Description" "of" "content"
    }

Ici, l’opération personnalisée body accepte un nombre variable d’arguments de différents types. Ceci est pris en charge par l’implémentation du générateur suivant, qui utilise la surcharge :

type Content = ArraySegment<byte> list

type ContentBuilder() =
    member _.Run(c: Content) =
        let crlf = "\r\n"B
        [|for part in List.rev c do
            yield! part.Array[part.Offset..(part.Count+part.Offset-1)]
            yield! crlf |]

    member _.Yield(_) = []

    [<CustomOperation("body")>]
    member _.Body(c: Content, segment: ArraySegment<byte>) =
        segment::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, bytes: byte[]) =
        ArraySegment<byte>(bytes, 0, bytes.Length)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, bytes: byte[], offset, count) =
        ArraySegment<byte>(bytes, offset, count)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, content: System.IO.Stream) =
        let mem = new System.IO.MemoryStream()
        content.CopyTo(mem)
        let bytes = mem.ToArray()
        ArraySegment<byte>(bytes, 0, bytes.Length)::c

    [<CustomOperation("body")>]
    member _.Body(c: Content, [<ParamArray>] contents: string[]) =
        List.rev [for c in contents -> let b = Text.Encoding.ASCII.GetBytes c in ArraySegment<_>(b,0,b.Length)] @ c

Cette fonctionnalité implémente F# RFC FS-1056.

Modèles « as »

Dans F# 6, le côté droit d’un modèle as peut désormais être lui-même un modèle. Cela est important lorsqu’un test de type a donné un type plus fort à une entrée. Considérons par exemple le code suivant :

type Pair = Pair of int * int

let analyzeObject (input: obj) =
    match input with
    | :? (int * int) as (x, y) -> printfn $"A tuple: {x}, {y}"
    | :? Pair as Pair (x, y) -> printfn $"A DU: {x}, {y}"
    | _ -> printfn "Nope"

let input = box (1, 2)

Dans chaque cas de modèle, l’objet d’entrée est soumis à un test de type. Le côté droit du modèle as peut maintenant être un autre modèle, qui peut lui-même correspondre à l’objet au type plus fort.

Cette fonctionnalité implémente F# RFC FS-1105.

Révisions de la syntaxe de mise en retrait

F# 6 supprime un certain nombre d’incohérences et de limitations dans son utilisation de la syntaxe de mise en retrait. Consultez RFC FS-1108. Cela résout 10 problèmes importants soulignés par les utilisateurs de F# depuis F# 4.0.

Par exemple, dans F# 5, le code suivant était autorisé :

let c = (
    printfn "aaaa"
    printfn "bbbb"
)

En revanche, le code suivant n’était pas autorisé (il générait un avertissement) :

let c = [
    1
    2
]

Dans F# 6, les deux sont autorisés. Cela facilite l’apprentissage de F# et simplifie son utilisation. Le contributeur de la communauté F# Hadrian Tang a ouvert la voie dans ce domaine, notamment en effectuant des tests systématiques remarquables et très utiles de cette fonctionnalité.

Cette fonctionnalité implémente F# RFC FS-1108.

Conversions implicites supplémentaires

Dans F# 6, nous avons activé la prise en charge d’autres conversions « implicites » et « dirigées par le type », comme décrit dans RFC FS-1093.

Cette modification offre trois avantages :

  1. Moins d’upcasts explicites sont nécessaires
  2. Moins de conversions explicites d’entiers sont nécessaires
  3. Ajout d’une prise en charge de première classe pour les conversions implicites de style .NET

Cette fonctionnalité implémente F# RFC FS-1093.

Conversions d’upcasts implicites supplémentaires

F# 6 implémente des conversions d’upcasts implicites supplémentaires. Par exemple, dans F# 5 et versions antérieures, les upcasts étaient nécessaires pour l’expression de retour lors de l’implémentation d’une fonction où les expressions disposaient de sous-types différents sur différentes branches, même lorsqu’une annotation de type était présente. Examinons le code F# 5 suivant :

open System
open System.IO

let findInputSource () : TextReader =
    if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
        // On Monday a TextReader
        Console.In
    else
        // On other days a StreamReader
        File.OpenText("path.txt") :> TextReader

Ici, les branches de la conditionnelle calculent respectivement un TextReader et un StreamReader, et l’upcast a été ajouté pour que les deux branches disposent du type StreamReader. Dans F# 6, ces upcasts sont désormais ajoutés automatiquement. Le code est donc plus simple :

let findInputSource () : TextReader =
    if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
        // On Monday a TextReader
        Console.In
    else
        // On other days a StreamReader
        File.OpenText("path.txt")

Vous pouvez éventuellement activer l’avertissement /warnon:3388 pour afficher un avertissement à chaque fois qu’un upcast implicite supplémentaire est utilisé, comme décrit dans Avertissements facultatifs pour les conversions implicites.

Conversions implicites d’entiers

Dans F# 6, les entiers de 32 bits sont élargis à des entiers de 64 bits lorsque les deux types sont connus. Par exemple, examinons une forme d’API classique :

type Tensor(…) =
    static member Create(sizes: seq<int64>) = Tensor(…)

Dans F# 5, les littéraux d’entier d’int64 doivent être utilisés :

Tensor.Create([100L; 10L; 10L])

ou

Tensor.Create([int64 100; int64 10; int64 10])

Dans F# 6, l’élargissement se produit automatiquement pour int32 vers int64, int32 vers nativeint et int32 vers double, lorsque les types de la source et de la destination sont connus pendant l’inférence de type. Ainsi, dans les cas tels que les exemples précédents, les littéraux int32 peuvent être utilisés :

Tensor.Create([100; 10; 10])

Malgré ce changement, dans la plupart des cas, F# continue d’utiliser l’élargissement explicite des types numériques. Par exemple, l’élargissement implicite ne s’applique pas aux autres types numériques, tels que int8 ou int16, ou de float32 vers float64, ou lorsque le type de la source ou de la destination est inconnu. Vous pouvez également activer l’avertissement /warnon:3389 pour afficher un avertissement à chaque fois qu’un élargissement numérique implicite est utilisé, comme décrit dans Avertissements facultatifs pour les conversions implicites.

Prise en charge de première classe pour les conversions implicites de style .NET

Dans F# 6, les conversions « op_Implicit » de .NET sont appliquées automatiquement dans le code F# lors de l’appel des méthodes. Par exemple, dans F# 5, il était nécessaire d’utiliser XName.op_Implicit lors de l’utilisation des API .NET pour XML :

open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")

Dans F# 6, les conversions op_Implicit sont appliquées automatiquement pour les expressions d’arguments lorsque les types sont disponibles pour l’expression source et le type cible :

open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")

Vous pouvez également activer l’avertissement /warnon:3395 pour afficher un avertissement à chaque fois qu’un élargissement des conversions op_Implicit est utilisé dans les arguments de méthode, comme décrit dans Avertissements facultatifs pour les conversions implicites.

Notes

Dans la première version de F# 6, le numéro de cet avertissement était /warnon:3390. Pour cause de conflit, ce numéro a ensuite été remplacé par le suivant : /warnon:3395.

Avertissements facultatifs pour les conversions implicites

Les conversions implicites et dirigées par le type peuvent mal interagir avec l’inférence de type et conduire à un code plus difficile à comprendre. C’est pourquoi il existe des mesures d’atténuation pour éviter que cette fonctionnalité ne soit utilisée de manière abusive dans le code F#. Premièrement, le type de la source et de la destination doit être parfaitement connu, sans ambiguïté ni inférence de type supplémentaire. Deuxièmement, des avertissements d’adhésion peuvent être activés pour signaler toute utilisation de conversions implicites, avec un avertissement activé par défaut :

  • /warnon:3388 (upcast implicite supplémentaire)
  • /warnon:3389 (élargissement numérique implicite)
  • /warnon:3391 (op_Implicit aux arguments hors méthode, activé par défaut)
  • /warnon:3395 (op_Implicit aux arguments de méthode)

Si votre équipe souhaite interdire l’utilisation des conversions implicites, vous pouvez également spécifier /warnaserror:3388, /warnaserror:3389, /warnaserror:3391 et /warnaserror:3395.

Mise en forme des nombres binaires

F# 6 ajoute le modèle %B aux spécificateurs de format disponibles pour les formats de nombres binaires. Examinons le code F# suivant :

printf "%o" 123
printf "%B" 123

Ce code imprime la sortie suivante :

173
1111011

Cette fonctionnalité implémente F# RFC FS-1100.

Abandons sur les liaisons d’utilisation

F# 6 permet d’utiliser _ dans une liaison use, par exemple :

let doSomething () =
    use _ = System.IO.File.OpenText("input.txt")
    printfn "reading the file"

Cette fonctionnalité implémente F# RFC FS-1102.

InlineIfLambda

Le compilateur F# comprend un optimiseur qui procède à l’inlining du code. Dans F# 6, nous avons ajouté une nouvelle fonctionnalité déclarative qui permet au code d’indiquer de manière facultative que, si un argument est identifié comme une fonction lambda, cet argument doit toujours être « inlined » dans les sites d’appel.

Par exemple, examinons la fonction iterateTwice suivante pour parcourir un tableau :

let inline iterateTwice ([<InlineIfLambda>] action) (array: 'T[]) =
    for j = 0 to array.Length-1 do
        action array[j]
    for j = 0 to array.Length-1 do
        action array[j]

Si le site d’appel est :

let arr = [| 1.. 100 |]
let mutable sum = 0
arr  |> iterateTwice (fun x ->
    sum <- sum + x)

Après l’inlining et d’autres optimisations, le code devient :

let arr = [| 1.. 100 |]
let mutable sum = 0
for j = 0 to arr.Length-1 do
    sum <- sum + arr[j]
for j = 0 to arr.Length-1 do
    sum <- sum + arr[j]

Contrairement aux versions précédentes de F#, cette optimisation est appliquée quelle que soit la taille de l’expression lambda concernée. Cette fonctionnalité peut également être utilisée pour implémenter un déroulement des boucles et des transformations similaires de manière plus fiable.

Un avertissement (/warnon:3517, désactivé par défaut) peut être activé pour indiquer les emplacements de votre code où les arguments InlineIfLambda ne sont pas liés aux expressions lambda sur les sites d’appel. En situation normale, cet avertissement ne doit pas être activé. Toutefois, dans certains types de programmation hautes performances, il peut être utile de s’assurer que tout le code est inlined et aplati.

Cette fonctionnalité implémente F# RFC FS-1098.

Code pouvant être repris

La prise en charge task {…} de F# 6 repose sur une base appelée codeRFC FS-1087 pouvant être repris. Le code pouvant être repris est une caractéristique technique qui peut être utilisée pour créer de nombreux types de machines à états asynchrones et à hautes performances.

Fonctions de collection supplémentaires

FSharp.Core 6.0.0 ajoute cinq nouvelles opérations aux fonctions de collection de base. Ces fonctions sont les suivantes :

  • List/Array/Seq.insertAt
  • List/Array/Seq.removeAt
  • List/Array/Seq.updateAt
  • List/Array/Seq.insertManyAt
  • List/Array/Seq.removeManyAt

Ces fonctions effectuent toutes des opérations de copie et de mise à jour sur le type de collection ou la séquence correspondante. Ce type d’opération est une forme de « mise à jour fonctionnelle ». Pour obtenir des exemples d’utilisation de ces fonctions, consultez la documentation correspondante, comme List.insertAt.

Par exemple, examinons le modèle, le message et la logique de mise à jour pour une application simple de type « Liste de tâches » écrite dans le style Elmish. Ici, l’utilisateur interagit avec l’application, ce qui génère des messages, et la fonction update traite ces messages, ce qui produit un nouveau modèle :

type Model =
    { ToDo: string list }

type Message =
    | InsertToDo of index: int * what: string
    | RemoveToDo of index: int
    | LoadedToDos of index: int * what: string list

let update (model: Model) (message: Message) =
    match message with
    | InsertToDo (index, what) ->
        { model with ToDo = model.ToDo |> List.insertAt index what }
    | RemoveToDo index ->
        { model with ToDo = model.ToDo |> List.removeAt index }
    | LoadedToDos (index, what) ->
        { model with ToDo = model.ToDo |> List.insertManyAt index what }

Avec ces nouvelles fonctions, la logique est claire et simple, et ne repose que sur des données immuables.

Cette fonctionnalité implémente F# RFC FS-1113.

La carte possède des clés et des valeurs

Dans FSharp.Core 6.0.0, le type Map prend désormais en charge les propriétés Keys et Values. Ces propriétés ne copient pas la collection sous-jacente.

Cette fonctionnalité est documentée dans F# RFC FS-1113.

Intrinsèques supplémentaires pour NativePtr

FSharp.Core 6.0.0 ajoute de nouveaux intrinsèques au module NativePtr :

  • NativePtr.nullPtr
  • NativePtr.isNullPtr
  • NativePtr.initBlock
  • NativePtr.clear
  • NativePtr.copy
  • NativePtr.copyBlock
  • NativePtr.ofILSigPtr
  • NativePtr.toILSigPtr

Comme les autres fonctions de NativePtr, ces fonctions sont inlined et leur utilisation émet des avertissements, sauf si /nowarn:9 est utilisé. L’utilisation de ces fonctions est limitée aux types non managés.

Cette fonctionnalité est documentée dans F# RFC FS-1109.

Types numériques supplémentaires avec annotations d’unités

Dans F# 6, les types ou alias d’abréviation de type suivants prennent désormais en charge les annotations d’unités de mesure. Les nouveaux ajouts sont affichés en gras :

Alias F# Type CLR
float32/single System.Single
float/double System.Double
decimal System.Decimal
sbyte/int8 System.SByte
int16 System.Int16
int/int32 System.Int32
int64 System.Int64
byte/uint8 System.Byte
uint16 System.UInt16
uint/uint32 System.UInt32
uint64 System.UIn64
nativeint System.IntPtr
unativeint System.UIntPtr

Par exemple, vous pouvez annoter un entier non signé comme suit :

[<Measure>]
type days

let better_age = 3u<days>

Cette fonctionnalité est documentée dans F# RFC FS-1091.

Avertissements informatifs pour les opérateurs symboliques rarement utilisés

F# 6 fournit une aide qui dénormalise l’utilisation de :=, !, incr et decr dans F# 6 et versions ultérieures. L’utilisation de ces opérateurs et fonctions génère des messages d’information qui vous demandent de remplacer votre code par une utilisation explicite de la propriété Value.

Dans la programmation en F#, les cellules de référence peuvent être utilisées pour les registres mutables alloués par tas. Bien qu’elles soient parfois utiles, elles sont rarement nécessaires dans le codage F# moderne, car let mutable peut être utilisé à la place. La bibliothèque principale F# comprend deux opérateurs := et !, et deux fonctions incr et decr spécifiquement liés aux appels de référence. La présence de ces opérateurs rend les cellules de référence plus centrales qu’elles ne devraient l’être dans la programmation en F#, ce qui oblige tous les programmeurs F# à connaître ces opérateurs. En outre, l’opérateur ! peut être facilement confondu avec l’opération not en C# ou autre langage, ce qui constitue une source potentiellement subtile de bogues lors de la traduction du code.

Ce changement a pour but de réduire le nombre d’opérateurs que le programmeur F# doit connaître, et donc de simplifier F# pour les débutants.

Par exemple, examinons le code F# 5 suivant :

let r = ref 0

let doSomething() =
    printfn "doing something"
    r := !r + 1

Tout d’abord, les cellules de référence sont rarement nécessaires dans le codage F# moderne, car let mutable peut normalement être utilisé à la place :

let mutable r = 0

let doSomething() =
    printfn "doing something"
    r <- r + 1

Si vous utilisez des cellules de référence, F# 6 émet un avertissement informatif vous demandant de remplacer la dernière ligne par r.Value <- r.Value + 1 et de consulter une aide supplémentaire sur l’utilisation appropriée des cellules de référence.

let r = ref 0

let doSomething() =
    printfn "doing something"
    r.Value <- r.Value + 1

Ces messages ne sont pas des avertissements ; il s’agit de « messages d’information » affichés dans l’IDE et dans la sortie du compilateur. F# reste rétrocompatible.

Cette fonctionnalité implémente F# RFC FS-1111.

Outil F# : .NET 6 par défaut pour l’écriture de scripts dans Visual Studio

Si vous ouvrez ou exécutez un script F# (.fsx) dans Visual Studio, par défaut, le script sera analysé et exécuté en utilisant .NET 6 avec une exécution 64 bits. Cette fonctionnalité, qui était en préversion dans les versions ultérieures à Visual Studio 2019, est désormais activée par défaut.

Pour activer l’écriture de scripts .NET Framework, sélectionnez Outils>Options>Outils F#>F# Interactive. Définissez Utiliser l’écriture de scripts .NET Core sur faux, puis redémarrez la fenêtre F# Interactive. Ce paramètre affecte à la fois la modification et l’exécution des scripts. Pour activer l’exécution 32 bits pour l’écriture de scripts .NET Framework, définissez également F# Interactive 64 bits sur faux. Il n’existe aucune option 32 bits pour l’écriture de scripts .NET Core.

Outil F# : Épingler la version du kit de développement logiciel (SDK) de vos scripts F#

Si vous exécutez un script à l’aide de dotnet fsi dans un répertoire contenant un fichier global.json avec un paramètre de kit SDK .NET, la version répertoriée du kit SDK .NET est utilisée pour exécuter et modifier le script. Cette fonctionnalité est disponible dans les versions ultérieures à F# 5.

Par exemple, supposons qu’un script se trouve dans un répertoire avec le fichier global.json suivant, qui spécifie une stratégie de version du kit SDK .NET :

{
  "sdk": {
    "version": "5.0.200",
    "rollForward": "minor"
  }
}

Si vous exécutez maintenant le script à l’aide de dotnet fsi, à partir de ce répertoire, la version du kit SDK est respectée. Cette fonctionnalité vous permet de « verrouiller » le kit SDK utilisé pour compiler, analyser et exécuter vos scripts.

Si vous ouvrez et modifiez votre script dans Visual Studio ou d’autres IDE, l’outil respecte ce paramètre lors de l’analyse et de la vérification de votre script. Si le kit SDK est introuvable, vous devez l’installer sur votre ordinateur de développement.

Sous Linux et d’autres systèmes Unix, vous pouvez combiner cela avec un shebang afin de spécifier également une version du langage pour l’exécution directe du script. Simple shebang pour script.fsx :

#!/usr/bin/env -S dotnet fsi

printfn "Hello, world"

À présent, le script peut être exécuté directement avec script.fsx. Vous pouvez combiner ceci avec une version spécifique du langage autre que la version par défaut en procédant comme suit :

#!/usr/bin/env -S dotnet fsi --langversion:5.0

Notes

Ce paramètre est ignoré par les outils d’édition, qui analysent le script en supposant que la dernière version du langage est utilisée.

Suppression des fonctionnalités héritées

Depuis F# 2.0, certaines fonctionnalités héritées déconseillées génèrent des avertissements. L’utilisation de ces fonctionnalités dans F# 6 génère des erreurs, sauf si vous utilisez explicitement /langversion:5.0. Les fonctionnalités qui génèrent des erreurs sont les suivantes :

  • Paramètres génériques multiples utilisant un nom de type suffixé, par exemple (int, int) Dictionary. Cela génère une erreur dans F# 6. La syntaxe Dictionary<int,int> standard doit être utilisée à la place.
  • #indent "off". Cela génère une erreur.
  • x.(expr). Cela génère une erreur.
  • module M = struct … end . Cela génère une erreur.
  • Utilisation des entrées *.ml et *.mli. Cela génère une erreur.
  • Utilisation de (*IF-CAML*) ou (*IF-OCAML*). Cela génère une erreur.
  • Utilisation de land, lor, lxor, lsl, lsr ou asr en tant qu’opérateurs infixe. Il s’agit de mots clés infixes en F# car il en était de même en OCaml et ils ne sont pas définis dans FSharp.Core. L’utilisation de ces mots clés déclenche désormais un avertissement.

Cela implémente F# RFC FS-1114.