Moduli

Nel contesto di F#, un modulo è un raggruppamento di codice F#, ad esempio valori, tipi e valori di funzione, in un programma F#. Il codice di raggruppamento nei moduli consente di tenere insieme il codice correlato e consente di evitare conflitti di nome nel programma.

Sintassi

// Top-level module declaration.
module [accessibility-modifier] [qualified-namespace.]module-name
declarations
// Local module declaration.
module [accessibility-modifier] module-name =
    declarations

Osservazioni:

Un modulo F# è un raggruppamento di costrutti di codice F#, ad esempio tipi, valori, valori di funzione e codice nelle do associazioni. Viene implementato come classe CLR (Common Language Runtime) che include solo membri statici. Esistono due tipi di dichiarazioni di modulo, a seconda che l'intero file sia incluso nel modulo: una dichiarazione di modulo di primo livello e una dichiarazione di modulo locale. Una dichiarazione di modulo di primo livello include l'intero file nel modulo. Una dichiarazione di modulo di primo livello può essere visualizzata solo come prima dichiarazione in un file.

Nella sintassi per la dichiarazione del modulo di primo livello, lo spazio dei nomi qualificato facoltativo è la sequenza di nomi annidati che contiene il modulo. Lo spazio dei nomi qualificato non deve essere dichiarato in precedenza.

Non è necessario rientro delle dichiarazioni in un modulo di primo livello. È necessario rientrare tutte le dichiarazioni nei moduli locali. In una dichiarazione di modulo locale, solo le dichiarazioni rientrate in tale dichiarazione di modulo fanno parte del modulo.

Se un file di codice non inizia con una dichiarazione di modulo di primo livello o una dichiarazione dello spazio dei nomi, l'intero contenuto del file, inclusi i moduli locali, diventa parte di un modulo di primo livello creato in modo implicito con lo stesso nome del file, senza l'estensione, con la prima lettera convertita in maiuscolo. Si consideri ad esempio il file seguente.

// In the file program.fs.
let x = 40

Questo file verrà compilato come se fosse stato scritto in questo modo:

module Program
let x = 40

Se sono presenti più moduli in un file, è necessario usare una dichiarazione di modulo locale per ogni modulo. Se viene dichiarato uno spazio dei nomi di inclusione, questi moduli fanno parte dello spazio dei nomi contenitore. Se uno spazio dei nomi di inclusione non viene dichiarato, i moduli diventano parte del modulo di primo livello creato in modo implicito. Nell'esempio di codice seguente viene illustrato un file di codice che contiene più moduli. Il compilatore crea in modo implicito un modulo di primo livello denominato Multiplemodulese viene MyModule1MyModule2 annidato in tale modulo di primo livello.

// In the file multiplemodules.fs.
// MyModule1
module MyModule1 =
    // Indent all program elements within modules that are declared with an equal sign.
    let module1Value = 100

    let module1Function x =
        x + 10

// MyModule2
module MyModule2 =

    let module2Value = 121

    // Use a qualified name to access the function.
    // from MyModule1.
    let module2Function x =
        x * (MyModule1.module1Function module2Value)

Se si dispone di più file in un progetto o in una singola compilazione o se si compila una libreria, è necessario includere una dichiarazione dello spazio dei nomi o una dichiarazione di modulo nella parte superiore del file. Il compilatore F# determina in modo implicito un nome di modulo solo quando è presente un solo file in un progetto o in una riga di comando di compilazione e si sta creando un'applicazione.

Il modificatore di accessibilità può essere uno dei seguenti: public, private, internal. Per altre informazioni, vedere Access Control. Il valore predefinito è public.

Riferimento al codice nei moduli

Quando si fa riferimento a funzioni, tipi e valori di un altro modulo, è necessario usare un nome completo o aprire il modulo. Se si usa un nome completo, è necessario specificare gli spazi dei nomi, il modulo e l'identificatore per l'elemento del programma desiderato. Ogni parte del percorso completo viene separata con un punto (.), come indicato di seguito.

Namespace1.Namespace2.ModuleName.Identifier

È possibile aprire il modulo o uno o più spazi dei nomi per semplificare il codice. Per altre informazioni sull'apertura di spazi dei nomi e moduli, vedere Dichiarazioni di importazione: parola open chiave.

Nell'esempio di codice seguente viene illustrato un modulo di primo livello che contiene tutto il codice fino alla fine del file.

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

Per usare questo codice da un altro file nello stesso progetto, è possibile usare nomi qualificati oppure aprire il modulo prima di usare le funzioni, come illustrato negli esempi seguenti.

// Fully qualify the function name.
let result1 = Arithmetic.add 5 9
// Open the module.
open Arithmetic
let result2 = add 5 9

Moduli annidati

I moduli possono essere annidati. I moduli interni devono essere rientrati per quanto riguarda le dichiarazioni di modulo esterno per indicare che sono moduli interni, non nuovi moduli. Ad esempio, confrontare i due esempi seguenti. Il modulo Z è un modulo interno nel codice seguente.

module Y =
    let x = 1

    module Z =
        let z = 5

Ma il modulo è un modulo ZY di pari livello nel codice seguente.

module Y =
    let x = 1

module Z =
    let z = 5

Il modulo Z è anche un modulo di pari livello nel codice seguente, perché non rientra per altre dichiarazioni nel modulo Y.

module Y =
        let x = 1

    module Z =
        let z = 5

Infine, se il modulo esterno non ha dichiarazioni e viene seguito immediatamente da un'altra dichiarazione di modulo, si presuppone che la nuova dichiarazione di modulo sia un modulo interno, ma il compilatore avvisa se la seconda definizione del modulo non è rientrata più lontano dalla prima.

// This code produces a warning, but treats Z as a inner module.
module Y =
module Z =
    let z = 5

Per eliminare l'avviso, impostare il rientro del modulo interno.

module Y =
    module Z =
        let z = 5

Se si vuole che tutto il codice in un file si trovi in un singolo modulo esterno e si desidera che i moduli interni non richiedano il segno di uguale e le dichiarazioni, incluse eventuali dichiarazioni di modulo interno, che verranno rientrate nel modulo esterno non devono essere rientrate. Le dichiarazioni all'interno delle dichiarazioni del modulo interno devono essere rientrate. Il codice seguente illustra questo caso.

// The top-level module declaration can be omitted if the file is named
// TopLevel.fs or topLevel.fs, and the file is the only file in an
// application.
module TopLevel

let topLevelX = 5

module Inner1 =
    let inner1X = 1
module Inner2 =
    let inner2X = 5

Moduli ricorsivi

F# 4.1 introduce la nozione di moduli che consentono a tutto il codice contenuto di essere ricorsivo a vicenda. Questa operazione viene eseguita tramite module rec. L'uso di module rec può alleviare alcuni dolori in non essere in grado di scrivere codice referenziale reciproca tra tipi e moduli. Di seguito è riportato un esempio:

module rec RecursiveModule =
    type Orientation = Up | Down
    type PeelState = Peeled | Unpeeled

    // This exception depends on the type below.
    exception DontSqueezeTheBananaException of Banana

    type Banana(orientation : Orientation) =
        member val IsPeeled = false with get, set
        member val Orientation = orientation with get, set
        member val Sides: PeelState list = [ Unpeeled; Unpeeled; Unpeeled; Unpeeled] with get, set

        member self.Peel() = BananaHelpers.peel self // Note the dependency on the BananaHelpers module.
        member self.SqueezeJuiceOut() = raise (DontSqueezeTheBananaException self) // This member depends on the exception above.

    module BananaHelpers =
        let peel (b: Banana) =
            let flip (banana: Banana) =
                match banana.Orientation with
                | Up ->
                    banana.Orientation <- Down
                    banana
                | Down -> banana

            let peelSides (banana: Banana) =
                banana.Sides
                |> List.map (function
                             | Unpeeled -> Peeled
                             | Peeled -> Peeled)

            match b.Orientation with
            | Up ->   b |> flip |> peelSides
            | Down -> b |> peelSides

Si noti che l'eccezione DontSqueezeTheBananaException e la classe Banana si riferiscono l'una all'altra. Inoltre, il modulo BananaHelpers e la classe Banana si riferiscono l'uno all'altro. Non sarebbe possibile esprimere in F# se la rec parola chiave è stata rimossa dal RecursiveModule modulo.

Questa funzionalità è anche possibile in Spazi dei nomi con F# 4.1.

Vedi anche