Moduły

W kontekście języka F# moduł jest grupowaniem kodu F#, takiego jak wartości, typy i wartości funkcji, w programie języka F#. Grupowanie kodu w modułach pomaga zachować powiązany kod razem i pomaga uniknąć konfliktów nazw w programie.

Składnia

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

Uwagi

Moduł języka F# to grupa konstrukcji kodu języka F#, takich jak typy, wartości, wartości funkcji i kod w do powiązaniach. Jest on implementowany jako klasa środowiska uruchomieniowego języka wspólnego (CLR), która ma tylko statyczne elementy członkowskie. Istnieją dwa typy deklaracji modułów, w zależności od tego, czy cały plik znajduje się w module: deklaracja modułu najwyższego poziomu i lokalna deklaracja modułu. Deklaracja modułu najwyższego poziomu zawiera cały plik w module. Deklaracja modułu najwyższego poziomu może być wyświetlana tylko jako pierwsza deklaracja w pliku.

W składni deklaracji modułu najwyższego poziomu opcjonalna przestrzeń nazw kwalifikowanych jest sekwencją zagnieżdżonych nazw przestrzeni nazw, które zawierają moduł. Kwalifikowana przestrzeń nazw nie musi być wcześniej zadeklarowana.

Nie trzeba wcięcia deklaracji w module najwyższego poziomu. Należy wcięć wszystkie deklaracje w modułach lokalnych. W deklaracji modułu lokalnego tylko deklaracje, które są wcięte w ramach tej deklaracji modułu, są częścią modułu.

Jeśli plik kodu nie rozpoczyna się od deklaracji modułu najwyższego poziomu lub deklaracji przestrzeni nazw, cała zawartość pliku, w tym wszystkie moduły lokalne, staje się częścią niejawnie utworzonego modułu najwyższego poziomu, który ma taką samą nazwę jak plik, bez rozszerzenia, z pierwszą literą przekonwertowaną na wielkie litery. Rozważmy na przykład następujący plik.

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

Ten plik zostanie skompilowany tak, jakby został zapisany w ten sposób:

module Program
let x = 40

Jeśli masz wiele modułów w pliku, musisz użyć deklaracji modułu lokalnego dla każdego modułu. Jeśli zadeklarowana jest otaczana przestrzeń nazw, te moduły są częścią otaczającej przestrzeni nazw. Jeśli otaczana przestrzeń nazw nie jest zadeklarowana, moduły stają się częścią niejawnie utworzonego modułu najwyższego poziomu. Poniższy przykład kodu przedstawia plik kodu zawierający wiele modułów. Kompilator niejawnie tworzy moduł najwyższego poziomu o nazwie Multiplemodulesi MyModule1 jest MyModule2 zagnieżdżony w tym module najwyższego poziomu.

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

Jeśli masz wiele plików w projekcie lub w jednej kompilacji lub jeśli tworzysz bibliotekę, musisz dołączyć deklarację przestrzeni nazw lub deklarację modułu w górnej części pliku. Kompilator języka F# określa tylko nazwę modułu niejawnie, gdy w wierszu polecenia projektu lub kompilacji znajduje się tylko jeden plik i tworzysz aplikację.

Modyfikator ułatwień dostępu może być jednym z następujących elementów: public, , privateinternal. Aby uzyskać więcej informacji, zobacz Kontrola dostępu. Wartość domyślna to publiczna.

Odwoływanie się do kodu w modułach

Jeśli odwołujesz się do funkcji, typów i wartości z innego modułu, musisz użyć kwalifikowanej nazwy lub otworzyć moduł. Jeśli używasz kwalifikowanej nazwy, musisz określić przestrzenie nazw, moduł i identyfikator żądanego elementu programu. Każda część kwalifikowanej ścieżki należy oddzielić kropką (.), w następujący sposób.

Namespace1.Namespace2.ModuleName.Identifier

Możesz otworzyć moduł lub co najmniej jedną przestrzeń nazw, aby uprościć kod. Aby uzyskać więcej informacji na temat otwierania przestrzeni nazw i modułów, zobacz Import Deklaracje: open Słowo kluczowe.

Poniższy przykład kodu przedstawia moduł najwyższego poziomu zawierający cały kod aż do końca pliku.

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

Aby użyć tego kodu z innego pliku w tym samym projekcie, należy użyć nazw kwalifikowanych lub otworzyć moduł przed użyciem funkcji, jak pokazano w poniższych przykładach.

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

Zagnieżdżone moduły

Moduły mogą być zagnieżdżone. Moduły wewnętrzne muszą być wcięte, jeśli chodzi o deklaracje modułów zewnętrznych, aby wskazać, że są modułami wewnętrznymi, a nie nowymi modułami. Porównaj na przykład następujące dwa przykłady. Moduł Z jest modułem wewnętrznym w poniższym kodzie.

module Y =
    let x = 1

    module Z =
        let z = 5

Jednak moduł Z jest elementem równorzędnym modułu Y w poniższym kodzie.

module Y =
    let x = 1

module Z =
    let z = 5

Moduł Z jest również modułem równorzędnym w poniższym kodzie, ponieważ nie jest on wcięty do innych deklaracji w module Y.

module Y =
        let x = 1

    module Z =
        let z = 5

Na koniec jeśli moduł zewnętrzny nie ma deklaracji i następuje natychmiast po innej deklaracji modułu, przyjmuje się, że nowa deklaracja modułu jest modułem wewnętrznym, ale kompilator wyświetli ostrzeżenie, jeśli druga definicja modułu nie jest wcięta dalej niż pierwsza.

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

Aby wyeliminować ostrzeżenie, wcięcie modułu wewnętrznego.

module Y =
    module Z =
        let z = 5

Jeśli chcesz, aby cały kod w pliku był w jednym module zewnętrznym i chcesz, aby moduły wewnętrzne, moduł zewnętrzny nie wymaga znaku równości, a deklaracje, w tym wszelkie deklaracje modułu wewnętrznego, które trafią do modułu zewnętrznego, nie muszą być wcięcia. Deklaracje wewnątrz deklaracji modułu wewnętrznego muszą być wcięte. Poniższy kod pokazuje ten przypadek.

// 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

Moduły cykliczne

Język F# 4.1 wprowadza pojęcie modułów, które umożliwiają wzajemnie rekursywny cały zawarty kod. Odbywa się to za pomocą polecenia module rec. Korzystanie z programu module rec może złagodzić niektóre problemy, ponieważ nie może pisać wzajemnie odwołującego się kodu między typami i modułami. Poniżej przedstawiono przykład:

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

Należy pamiętać, że wyjątek DontSqueezeTheBananaException i klasa Banana odwołują się do siebie nawzajem. Ponadto moduł BananaHelpers i klasa Banana również odwołują się do siebie. Nie byłoby to możliwe do wyrażenia w języku F# w przypadku usunięcia rec słowa kluczowego z modułu RecursiveModule .

Ta funkcja jest również możliwa w przestrzeniach nazw w języku F# 4.1.

Zobacz też