Module

Im Kontext von F# handelt es sich bei einem Modul um eine Gruppierung von F#-Code (z. B. Werte, Typen und Funktionswerte in einem F#-Programm). Das Gruppieren von Code in Modulen hilft dabei, verwandten Code zusammen zu halten und Namenskonflikte in Ihrem Programm zu vermeiden.

Syntax

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

Bemerkungen

Ein F#-Modul ist eine Gruppierung von F#-Codekonstrukten (z. B. Werte, Typen, Funktionswerte und Code in do-Bindungen). Es wird als CLR-Klasse (Common Language Runtime) implementiert, die nur statische Member enthält. Es gibt zwei Typen von Moduldeklarationen, je nachdem, ob die gesamte Datei im Modul enthalten ist: eine Moduldeklaration der obersten Ebene und eine lokale Moduldeklaration. Eine Moduldeklaration der obersten Ebene enthält die gesamte Datei im Modul. Sie kann nur als erste Deklaration in einer Datei angezeigt werden.

In der Syntax für die Moduldeklaration der obersten Ebene ist der optionale qualifizierte Namespace die Sequenz geschachtelter Namespacenamen, die das Modul enthält. Der qualifizierte Namespace muss nicht zuvor deklariert werden.

In einem Modul der obersten Ebene müssen Sie keine Deklarationen einrücken. Bei lokalen Modulen müssen Sie alle Deklarationen einrücken. In einer lokalen Moduldeklaration sind nur die Deklarationen, die unter dieser Moduldeklaration eingerückt werden, Teil des Moduls.

Wenn eine Codedatei nicht mit einer Moduldeklaration der obersten Ebene oder einer Namespacedeklaration beginnt, wird der gesamte Inhalt der Datei (einschließlich aller lokalen Module) Teil eines implizit erstellten Moduls der obersten Ebene, das denselben Namen wie die Datei aufweist, jedoch ohne die Erweiterung und mit in Großbuchstaben konvertiertem ersten Buchstaben. Sehen Sie sich beispielsweise die folgende Datei an.

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

Diese Datei würde so kompiliert werden, als würde sie wie folgt geschrieben werden:

module Program
let x = 40

Wenn Sie über mehrere Module in einer Datei verfügen, müssen Sie für jedes Modul eine lokale Moduldeklaration verwenden. Wenn ein einschließender Namespace deklariert wird, sind diese Module Teil des einschließenden Namespace. Wenn kein einschließender Namespace deklariert wird, werden die Module Teil des implizit erstellten Moduls der obersten Ebene. Das folgende Codebeispiel zeigt eine Codedatei, die mehrere Module enthält. Der Compiler erstellt implizit ein Modul der obersten Ebene mit dem Namen Multiplemodules, und MyModule1 sowie MyModule2 werden in diesem Modul der obersten Ebene geschachtelt.

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

Wenn Sie mehrere Dateien in einem Projekt oder in einer einzigen Kompilierung haben oder wenn Sie eine Bibliothek erstellen, müssen Sie oben in der Datei eine Namespacedeklaration oder Moduldeklaration einfügen. Der F#-Compiler bestimmt einen Modulnamen nur dann implizit, wenn nur eine Datei in einer Projekt- oder Kompilierungsbefehlszeile vorhanden ist und Sie eine Anwendung erstellen.

Der Zugriffsmodifizierer kann Folgendes sein: public, private oder internal. Weitere Informationen finden Sie unter Zugriffssteuerung. Der Standardwert ist public.

Verweisen auf Code in Modulen

Wenn Sie auf Funktionen, Typen und Werte aus einem anderen Modul verweisen, müssen Sie entweder einen qualifizierten Namen verwenden oder das Modul öffnen. Wenn Sie einen qualifizierten Namen verwenden, müssen Sie die Namespaces, das Modul und den Bezeichner für das gewünschte Programmelement angeben. Sie trennen jeden Teil des qualifizierten Pfads wie folgt mit einem Punkt („.“).

Namespace1.Namespace2.ModuleName.Identifier

Sie können das Modul oder mindestens einen der Namespaces öffnen, um den Code zu vereinfachen. Weitere Informationen zum Öffnen von Namespaces und Modulen finden Sie unter Importdeklarationen: open-Schlüsselwort.

Das folgende Codebeispiel zeigt ein Modul der obersten Ebene, das den gesamten Code bis zum Ende der Datei enthält.

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

Wenn Sie diesen Code aus einer anderen Datei im selben Projekt verwenden möchten, verwenden Sie wie in den folgenden Beispielen gezeigt entweder qualifizierte Namen, oder öffnen Sie das Modul, bevor Sie die Funktionen verwenden.

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

Geschachtelte Module

Module können geschachtelt werden. Innere Module müssen so weit eingerückt werden wie äußere Moduldeklarationen, um anzugeben, dass es sich um innere Module und nicht um neue Module handelt. Vergleichen Sie beispielsweise die folgenden beiden Beispiele. Modul Z ist ein inneres Modul im folgenden Code.

module Y =
    let x = 1

    module Z =
        let z = 5

Modul Z ist jedoch ein gleichgeordnetes Modul zu Modul Y im folgenden Code.

module Y =
    let x = 1

module Z =
    let z = 5

Modul Z ist auch ein gleichgeordnetes Modul im folgenden Code, da es nicht so weit wie andere Deklarationen im Modul Y eingerückt wird.

module Y =
        let x = 1

    module Z =
        let z = 5

Wenn das äußere Modul schließlich keine Deklarationen aufweist und unmittelbar darauf eine andere Moduldeklaration folgt, wird davon ausgegangen, dass die neue Moduldeklaration ein inneres Modul ist. Der Compiler warnt Sie jedoch, wenn die zweite Moduldefinition nicht weiter als die erste eingerückt wird.

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

Um die Warnung zu entfernen, rücken Sie das innere Modul ein.

module Y =
    module Z =
        let z = 5

Wenn sich der gesamte Code in einer Datei in einem einzigen äußeren Modul befinden soll und Sie innere Module benötigen, erfordert das äußere Modul nicht das Gleichheitszeichen, und die Deklarationen (einschließlich aller inneren Moduldeklarationen), die in das äußere Modul verschoben werden, müssen nicht eingerückt werden. Deklarationen innerhalb der inneren Moduldeklarationen müssen eingerückt werden. Der folgende Code veranschaulicht diesen Fall.

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

Rekursive Module

In F# 4.1 wird das Konzept der Module eingeführt, die es ermöglichen, dass der gesamte enthaltene Code wechselseitig rekursiv ist. Dies erfolgt über module rec. Durch die Verwendung von module rec können einige Probleme umgangen werden, die dadurch entstehen, dass kein gegenseitig referenzieller Code zwischen Typen und Modulen geschrieben werden kann. Im Folgenden sehen Sie ein Beispiel dafür:

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

Beachten Sie, dass die Ausnahme DontSqueezeTheBananaException und die Klasse Banana aufeinander verweisen. Darüber hinaus verweisen auch das Modul BananaHelpers und die Klasse Banana aufeinander. Wenn Sie das rec-Schlüsselwort aus dem RecursiveModule-Modul entfernen würden, könnte dies nicht in F# ausgedrückt werden.

Diese Funktion ist auch in Namespaces mit F# 4.1 möglich.

Weitere Informationen