Novità in F# 6

F# 6 aggiunge diversi miglioramenti al linguaggio F# e F# Interactive. Viene rilasciato con .NET 6.

È possibile scaricare la versione più recente di .NET SDK dalla pagina di download di .NET.

Operazioni preliminari

F# 6 è disponibile in tutte le distribuzioni di .NET Core e gli strumenti di Visual Studio. Per altre informazioni, vedere Introduzione a F#.

attività {...}

F# 6 include il supporto nativo per la creazione di attività .NET nel codice F#. Si consideri ad esempio il codice F# seguente per creare un oggetto . Attività compatibile con 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

Usando F# 6, questo codice può essere riscritto come segue.

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

Il supporto delle attività era disponibile per F# 5 tramite le eccellenti librerie TaskBuilder.fs e Ply. Dovrebbe essere semplice eseguire la migrazione del codice al supporto predefinito. Esistono tuttavia alcune differenze: gli spazi dei nomi e l'inferenza dei tipi differiscono leggermente tra il supporto predefinito e queste librerie e potrebbero essere necessarie alcune annotazioni di tipo aggiuntive. Se necessario, è comunque possibile usare queste librerie della community con F# 6 se si fa riferimento in modo esplicito e si aprono gli spazi dei nomi corretti in ogni file.

L'uso task {…} di è molto simile all'uso async {…}di . L'uso di task {…} presenta diversi vantaggi rispetto async {…}a :

  • Il sovraccarico di task {...} è inferiore, possibilmente migliorando le prestazioni nei percorsi di codice ad accesso frequente in cui il lavoro asincrono viene eseguito rapidamente.
  • Il debug di istruzioni e analisi dello stack per task {…} è migliore.
  • L'interoperabilità con i pacchetti .NET che prevedono o producono attività è più semplice.

Se si ha familiarità con async {…}, esistono alcune differenze da tenere presenti:

  • task {…} esegue immediatamente l'attività al primo punto await.
  • task {…} non propaga in modo implicito un token di annullamento.
  • task {…} non esegue controlli di annullamento impliciti.
  • task {…} non supporta le chiamate di coda asincrone. Ciò significa che l'uso return! .. ricorsivo può comportare overflow dello stack se non sono presenti attese asincrone intermedie.

In generale, è consigliabile usare task {…} over async {…} in un nuovo codice se si interagisce con le librerie .NET che usano attività e se non ci si basa su codecall asincrone o la propagazione implicita dei token di annullamento. Nel codice esistente, è consigliabile passare a task {…} solo dopo aver esaminato il codice per assicurarsi di non basarsi sulle caratteristiche indicate in precedenza di async {…}.

Questa funzionalità implementa F# RFC FS-1097.

Sintassi di indicizzazione più semplice con expr[idx]

F# 6 consente la sintassi expr[idx] per l'indicizzazione e il sezionamento di raccolte.

Fino a e includendo F# 5, F# ha usato expr.[idx] come sintassi di indicizzazione. Consentire l'uso di expr[idx] è basato sul feedback ripetuto di questi F# di apprendimento o sulla visualizzazione di F# per la prima volta che l'uso dell'indicizzazione con notazione di punti si presenta come una divergenza non necessaria rispetto alla pratica standard del settore.

Non si tratta di una modifica che causa un'interruzione perché per impostazione predefinita non vengono generati avvisi sull'uso di expr.[idx]. Tuttavia, vengono generati alcuni messaggi informativi che suggeriscono chiarimenti sul codice. Facoltativamente, è possibile attivare anche altri messaggi informativi. Ad esempio, è possibile attivare un avviso informativo facoltativo (/warnon:3566) per avviare l'uso della segnalazione della expr.[idx] notazione. Per altre informazioni, vedere Notazione dell'indicizzatore.

Nel nuovo codice si consiglia l'uso sistematico di come sintassi di expr[idx] indicizzazione.

Questa funzionalità implementa F# RFC FS-1110.

Rappresentazioni di struct per modelli attivi parziali

F# 6 aumenta la funzionalità "modelli attivi" con rappresentazioni di struct facoltative per modelli attivi parziali. In questo modo è possibile usare un attributo per vincolare un modello attivo parziale per restituire un'opzione di valore:

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

L'uso dell'attributo è obbligatorio. Nei siti di utilizzo il codice non cambia. Il risultato netto è che le allocazioni vengono ridotte.

Questa funzionalità implementa F# RFC FS-1039.

Operazioni personalizzate di overload nelle espressioni di calcolo

F# 6 consente di usare CustomOperationAttribute nei metodi di overload.

Si consideri l'uso seguente di un generatore contentdi espressioni di calcolo:

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

In questo caso l'operazione body personalizzata accetta un numero variabile di argomenti di tipi diversi. Questa funzionalità è supportata dall'implementazione del generatore seguente, che usa l'overload:

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

Questa funzionalità implementa F# RFC FS-1056.

Modelli "as"

In F# 6 il lato destro di un as modello può ora essere un modello. Questo aspetto è importante quando un test di tipo ha dato un tipo più forte a un input. Si consideri il codice di esempio seguente:

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)

In ogni caso del modello, l'oggetto di input viene testato dal tipo. Il lato destro del as modello ora può essere un altro modello, che può corrispondere all'oggetto con il tipo più forte.

Questa funzionalità implementa F# RFC FS-1105.

Revisioni della sintassi di rientro

F# 6 rimuove una serie di incoerenze e limitazioni nell'uso della sintassi con riconoscimento del rientro. Vedere RFC FS-1108. Questo risolve 10 problemi significativi evidenziati dagli utenti F# a partire da F# 4.0.

Ad esempio, in F# 5 è stato consentito il codice seguente:

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

Tuttavia, il codice seguente non è consentito (ha generato un avviso):

let c = [
    1
    2
]

In F# 6 sono consentiti entrambi. In questo modo, F# risulta più semplice e facile da apprendere. Il collaboratore della community F# Hadrian Tang ha condotto a questo scopo, tra cui test sistematici notevoli e estremamente preziosi della funzionalità.

Questa funzionalità implementa F# RFC FS-1108.

Conversioni implicite aggiuntive

In F# 6 è stato attivato il supporto per altre conversioni "implicite" e "dirette ai tipi", come descritto in RFC FS-1093.

Questa modifica offre tre vantaggi:

  1. Sono necessari meno upcast espliciti
  2. Sono necessarie meno conversioni di numeri interi espliciti
  3. Supporto di prima classe per . Sono state aggiunte conversioni implicite in stile NET

Questa funzionalità implementa F# RFC FS-1093.

Conversioni di upcast implicite aggiuntive

F# 6 implementa conversioni di upcast implicite aggiuntive. Ad esempio, in F# 5 e versioni precedenti erano necessari upcast per l'espressione restituita durante l'implementazione di una funzione in cui le espressioni avevano sottotipi diversi in rami diversi, anche quando era presente un'annotazione di tipo. Si consideri il codice F# 5 seguente:

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

Qui i rami del calcolo condizionale e TextReaderStreamReader rispettivamente e il upcast è stato aggiunto per fare in modo che entrambi i rami abbiano tipo StreamReader. In F# 6 questi upcast vengono aggiunti automaticamente. Ciò significa che il codice è più semplice:

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

Facoltativamente, è possibile abilitare l'avviso /warnon:3388 per visualizzare un avviso in ogni punto in cui viene usato un upcast implicito aggiuntivo, come descritto in Avvisi facoltativi per le conversioni implicite.

Conversioni intere implicite

In F# 6, i numeri interi a 32 bit vengono ampliati a interi a 64 bit quando entrambi i tipi sono noti. Si consideri ad esempio una forma API tipica:

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

In F# 5 è necessario usare i valori letterali integer per int64:

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

or

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

In F# 6, l'estensione avviene automaticamente per int32 a int64, int32 a nativeinte int32 a double, quando il tipo di origine e di destinazione è noto durante l'inferenza del tipo. Pertanto, nei casi come gli esempi precedenti, int32 è possibile usare i valori letterali:

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

Nonostante questa modifica, F# continua a usare l'ampliamento esplicito dei tipi numerici nella maggior parte dei casi. Ad esempio, l'estensione implicita non si applica ad altri tipi numerici, ad esempio int8 o int16, o da float32 a float64o quando il tipo di origine o di destinazione è sconosciuto. Facoltativamente, è anche possibile abilitare l'avviso /warnon:3389 per visualizzare un avviso in ogni punto viene usato un tipo di estensione numerica implicita, come descritto in Avvisi facoltativi per le conversioni implicite.

Supporto di prima classe per . Conversioni implicite in stile NET

In F# 6 le conversioni "op_Implicit" di .NET vengono applicate automaticamente nel codice F# quando si chiamano i metodi. Ad esempio, in F# 5 era necessario usare quando si usano XName.op_Implicit le API .NET per XML:

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

In F# 6 le op_Implicit conversioni vengono applicate automaticamente per le espressioni di argomento quando i tipi sono disponibili per l'espressione di origine e il tipo di destinazione:

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

Facoltativamente, è possibile abilitare l'avviso /warnon:3395 per visualizzare un avviso in ogni punto op_Implicit in cui le conversioni di tipo widening vengono usate negli argomenti del metodo, come descritto in Avvisi facoltativi per le conversioni implicite.

Nota

Nella prima versione di F# 6 questo numero di avviso era /warnon:3390. A causa di un conflitto, il numero di avviso è stato aggiornato successivamente a /warnon:3395.

Avvisi facoltativi per le conversioni implicite

Le conversioni implicite e dirette dai tipi possono interagire in modo non adeguato con l'inferenza del tipo e portare a codice più difficile da comprendere. Per questo motivo, esistono alcune mitigazioni per garantire che questa funzionalità non venga abusata nel codice F#. Prima di tutto, sia il tipo di origine che quello di destinazione devono essere fortemente noti, senza ambiguità o inferenza aggiuntiva del tipo derivante. In secondo luogo, gli avvisi di consenso esplicito possono essere attivati per segnalare qualsiasi uso di conversioni implicite, con un avviso attivato per impostazione predefinita:

  • /warnon:3388 (upcast implicito aggiuntivo)
  • /warnon:3389 (ampliamento numerico implicito)
  • /warnon:3391 (op_Implicit argomenti non di metodo, in base all'impostazione predefinita)
  • /warnon:3395 (op_Implicit agli argomenti del metodo)

Se il team vuole vietare tutti gli usi di conversioni implicite, è anche possibile specificare /warnaserror:3388, /warnaserror:3391/warnaserror:3389, e /warnaserror:3395.

Formattazione per i numeri binari

F# 6 aggiunge il %B modello agli identificatori di formato disponibili per i formati numerici binari. Prendere in considerazione il codice F# seguente:

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

Questo codice stampa l'output seguente:

173
1111011

Questa funzionalità implementa F# RFC FS-1100.

Elimina le associazioni in caso di utilizzo

F# 6 consente di _ essere usato in un'associazione use , ad esempio:

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

Questa funzionalità implementa F# RFC FS-1102.

InlineIfLambda

Il compilatore F# include un ottimizzatore che esegue l'inlining del codice. In F# 6 è stata aggiunta una nuova funzionalità dichiarativa che consente al codice di indicare facoltativamente che, se un argomento è determinato come una funzione lambda, tale argomento deve essere sempre inlined nei siti di chiamata.

Si consideri ad esempio la funzione seguente iterateTwice per attraversare una matrice:

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]

Se il sito di chiamata è:

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

Dopo l'inlining e altre ottimizzazioni, il codice diventa:

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]

A differenza delle versioni precedenti di F#, questa ottimizzazione viene applicata indipendentemente dalle dimensioni dell'espressione lambda coinvolta. Questa funzionalità può essere usata anche per implementare l'annullamento della registrazione del ciclo e trasformazioni simili in modo più affidabile.

È possibile attivare un avviso di consenso esplicito (/warnon:3517disattivato per impostazione predefinita) per indicare le posizioni nel codice in cui InlineIfLambda gli argomenti non sono associati alle espressioni lambda nei siti di chiamata. In situazioni normali, questo avviso non deve essere abilitato. Tuttavia, in alcuni tipi di programmazione ad alte prestazioni, può essere utile assicurarsi che tutto il codice sia inlined e appiattito.

Questa funzionalità implementa F# RFC FS-1098.

Codice ripristinabile

Il task {…} supporto di F# 6 è basato su una base denominata codiceripristinabile RFC FS-1087. Il codice ripristinabile è una funzionalità tecnica che può essere usata per compilare molti tipi di macchine a stati asincrone e a prestazioni elevate.

Funzioni di raccolta aggiuntive

FSharp.Core 6.0.0 aggiunge cinque nuove operazioni alle funzioni di raccolta principali. Queste funzioni sono:

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

Queste funzioni eseguono tutte operazioni di copia e aggiornamento sul tipo o sequenza di raccolta corrispondente. Questo tipo di operazione è una forma di "aggiornamento funzionale". Per esempi di utilizzo di queste funzioni, vedere la documentazione corrispondente, ad esempio List.insertAt.

Si consideri ad esempio il modello, il messaggio e la logica di aggiornamento per una semplice applicazione "Todo List" scritta nello stile Elmish. Qui l'utente interagisce con l'applicazione, generando messaggi e la update funzione elabora questi messaggi, generando un nuovo modello:

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 }

Con queste nuove funzioni, la logica è chiara e semplice e si basa solo su dati non modificabili.

Questa funzionalità implementa F# RFC FS-1113.

Map ha chiavi e valori

In FSharp.Core 6.0.0 il Map tipo supporta ora le proprietà Keys and Values . Queste proprietà non copiano la raccolta sottostante.

Questa funzionalità è documentata in F# RFC FS-1113.

Intrinseci aggiuntivi per NativePtr

FSharp.Core 6.0.0 aggiunge nuovi intrinseci al modulo NativePtr :

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

Come con altre funzioni in NativePtr, queste funzioni vengono inlinede e il loro uso genera avvisi a meno che non /nowarn:9 venga usato. L'uso di queste funzioni è limitato ai tipi non gestiti.

Questa funzionalità è documentata in F# RFC FS-1109.

Tipi numerici aggiuntivi con annotazioni di unità

In F# 6 i tipi o gli alias di abbreviazione del tipo seguenti supportano ora annotazioni unit-of-measure. Le nuove aggiunte sono visualizzate in grassetto:

Alias F# Tipo 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

Ad esempio, è possibile annotare un intero senza segno come indicato di seguito:

[<Measure>]
type days

let better_age = 3u<days>

Questa funzionalità è documentata in F# RFC FS-1091.

Avvisi informativi per operatori simbolici usati raramente

F# 6 aggiunge linee guida soft che de normalizzano l'uso di :=, !, incre decr in F# 6 e versioni successive. L'uso di questi operatori e funzioni genera messaggi informativi che richiedono di sostituire il codice con l'uso esplicito della Value proprietà .

Nella programmazione F# è possibile usare le celle di riferimento per i registri modificabili allocati dall'heap. Anche se occasionalmente sono utili, sono raramente necessari nella codifica F# moderna, perché let mutable può essere invece usata. La libreria principale F# include due operatori := e ! due funzioni incr e decr in particolare correlate alle chiamate di riferimento. La presenza di questi operatori rende le celle di riferimento più centrali della programmazione F# rispetto a quanto necessario, richiedendo a tutti i programmatori F# di conoscere questi operatori. Inoltre, l'operatore ! può essere facilmente confuso con l'operazione in C# e altri linguaggi, una fonte potenzialmente sottile di bug durante la not traduzione del codice.

La logica di questa modifica consiste nel ridurre il numero di operatori che il programmatore F# deve conoscere e quindi semplificare F# per principianti.

Si consideri ad esempio il codice F# 5 seguente:

let r = ref 0

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

Prima di tutto, le celle di riferimento sono raramente necessarie nella codifica F# moderna, come let mutable in genere è possibile usare:

let mutable r = 0

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

Se si usano celle di riferimento, F# 6 genera un avviso informativo che chiede di modificare l'ultima riga in r.Value <- r.Value + 1e di collegarsi a ulteriori indicazioni sull'uso appropriato delle celle di riferimento.

let r = ref 0

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

Questi messaggi non sono avvisi; sono "messaggi informativi" visualizzati nell'IDE e nell'output del compilatore. F# rimane compatibile con le versioni precedenti.

Questa funzionalità implementa F# RFC FS-1111.

Strumenti F#: .NET 6 l'impostazione predefinita per lo scripting in Visual Studio

Se si apre o si esegue uno script F# (.fsx) in Visual Studio, per impostazione predefinita lo script verrà analizzato ed eseguito usando .NET 6 con l'esecuzione a 64 bit. Questa funzionalità era disponibile in anteprima nelle versioni successive di Visual Studio 2019 ed è ora abilitata per impostazione predefinita.

Per abilitare lo scripting di .NET Framework, selezionare Strumenti>Opzioni>F# Strumenti>F# Interactive. Impostare Usa scripting di .NET Core su false e quindi riavviare la finestra interattiva F#. Questa impostazione influisce sia sulla modifica dello script che sull'esecuzione di script. Per abilitare l'esecuzione a 32 bit per gli script di .NET Framework, impostare anche F# Interactive a 64 bit su false. Non è disponibile alcuna opzione a 32 bit per lo scripting di .NET Core.

Strumenti F#: Aggiungere la versione SDK degli script F#

Se si esegue uno script usando dotnet fsi in una directory contenente un file global.json con un'impostazione .NET SDK, la versione elencata di .NET SDK verrà usata per eseguire e modificare lo script. Questa funzionalità è stata disponibile nelle versioni successive di F# 5.

Si supponga, ad esempio, che in una directory sia presente uno script con il file global.json seguente che specifica un criterio di versione di .NET SDK:

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

Se ora si esegue lo script usando dotnet fsi, da questa directory verrà rispettata la versione dell'SDK. Si tratta di una funzionalità potente che consente di "bloccare" l'SDK usato per compilare, analizzare ed eseguire gli script.

Se si apre e si modifica lo script in Visual Studio e in altri IDE, gli strumenti rispettano questa impostazione durante l'analisi e il controllo dello script. Se l'SDK non viene trovato, sarà necessario installarlo nel computer di sviluppo.

In Linux e in altri sistemi Unix, è possibile combinare questo elemento con un shepoint per specificare anche una versione del linguaggio per l'esecuzione diretta dello script. Una semplice shebang per script.fsx è:

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

printfn "Hello, world"

Ora lo script può essere eseguito direttamente con script.fsx. È possibile combinare questa opzione con una versione della lingua specifica e non predefinita, come illustrato di seguito:

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

Nota

Questa impostazione viene ignorata modificando gli strumenti, che analizzeranno lo script presupponendo la versione più recente del linguaggio.

Rimozione delle funzionalità legacy

A partire da F# 2.0, alcune funzionalità legacy deprecate presentano lunghi avvisi. L'uso di queste funzionalità in F# 6 genera errori a meno che non si usi /langversion:5.0in modo esplicito . Le funzionalità che forniscono errori sono:

  • Più parametri generici che usano un nome di tipo suffisso, ad esempio (int, int) Dictionary. Si tratta di un errore in F# 6. È consigliabile usare invece la sintassi Dictionary<int,int> standard.
  • #indent "off". Si tratta di un errore.
  • x.(expr). Si tratta di un errore.
  • module M = struct … end . Si tratta di un errore.
  • Uso di input *.ml e *.mli. Si tratta di un errore.
  • Uso di (*IF-CAML*) o (*IF-OCAML*). Si tratta di un errore.
  • Uso di land, lorlxor, lsl, , lsro asr come operatori di prefisso. Si tratta di parole chiave infix in F# perché sono parole chiave infix in OCaml e non sono definite in FSharp.Core. L'uso di queste parole chiave genera ora un avviso.

Ciò implementa F# RFC FS-1114.