在 F# 的內容中, 模組 是 F# 程式代碼的群組,例如 F# 程式中的值、類型和函式值。 將模組中的程式代碼分組有助於將相關的程式代碼保持在一起,並協助避免程式的名稱衝突。
語法
// Top-level module declaration.
module [accessibility-modifier] [qualified-namespace.]module-name
declarations
// Local module declaration.
module [accessibility-modifier] module-name =
declarations
備註
F# 模組是 F# 程式代碼建構的群組,例如系結中的 do 類型、值、函式值和程式代碼。 它實作為只有靜態成員的 Common Language Runtime (CLR) 類別。 模組宣告有兩種類型,取決於整個檔案是否包含在模組中:最上層模組宣告和本機模組宣告。 最上層模組宣告包含模組中的整個檔案。 最上層模組宣告只能顯示為檔案中的第一個宣告。
在最上層模組宣告的語法中,選擇性 限定命名空間 是包含模組的巢狀命名空間名稱序列。 限定的命名空間不需要先前宣告。
您不需要在最上層模組中縮排宣告。 您必須縮排本機模組中的所有宣告。 在本機模組宣告中,只有縮排在該模組宣告底下的宣告是模組的一部分。
如果程式代碼檔案不是以最上層模組宣告或命名空間宣告開頭,則檔案的完整內容,包括任何本機模組,都會成為隱含建立的最上層模組的一部分,該模組的名稱與檔案同名,不含擴展名,且第一個字母轉換成大寫。 例如,請考慮下列檔案。
// In the file program.fs.
let x = 40
此檔案的編譯方式會如同以這種方式撰寫:
module Program
let x = 40
如果您在檔案中有多個模組,則必須針對每個模組使用本機模組宣告。 如果宣告封入命名空間,這些模組是封入命名空間的一部分。 如果未宣告封入命名空間,模組會成為隱含建立的最上層模組的一部分。 下列程式代碼範例顯示包含多個模組的程式代碼檔案。 編譯程式會隱含地建立名為 Multiplemodules的頂層模組,且 MyModule1MyModule2 會巢狀在該最上層模組中。
// 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)
如果您在專案或單一編譯中有多個檔案,或您要建置連結庫,則必須在檔案頂端包含命名空間宣告或模組宣告。 當專案或編譯命令行中只有一個檔案,而且您正在建立應用程式時,F# 編譯程式只會隱含判斷模組名稱。
輔助功能修飾詞可以是下列其中一項:public、private、 internal。 如需詳細資訊,請參閱 存取控制。 預設值是公用。
參考模組中的程序代碼
當您參考來自另一個模組的函式、類型和值時,您必須使用限定名稱或開啟模組。 如果您使用限定名稱,則必須指定您想要之程式專案的命名空間、模組和識別碼。 您可以使用點 (.) 分隔限定路徑的每個部分,如下所示。
Namespace1.Namespace2.ModuleName.Identifier
您可以開啟模組或一或多個命名空間,以簡化程序代碼。 如需開啟命名空間和模組的詳細資訊,請參閱 匯入宣告: open 關鍵詞。
下列程式代碼範例顯示最上層模組,其中包含檔案結尾的所有程序代碼。
module Arithmetic
let add x y =
x + y
let sub x y =
x - y
若要從相同專案中的另一個檔案使用此程序代碼,請使用限定的名稱,或是在使用函式之前開啟模組,如下列範例所示。
// Fully qualify the function name.
let result1 = Arithmetic.add 5 9
// Open the module.
open Arithmetic
let result2 = add 5 9
巢狀模組
模組可以是巢狀的。 內部模組必須縮排至外部模組宣告,以指出它們是內部模組,而不是新的模組。 例如,比較下列兩個範例。 模組 Z 是下列程式代碼中的內部模組。
module Y =
let x = 1
module Z =
let z = 5
但module Z 是下列程式代碼中模組 Y 的同層級。
module Y =
let x = 1
module Z =
let z = 5
Module Z 也是下列程式代碼中的同層級模組,因為它不會縮排模組中的其他 Y宣告。
module Y =
let x = 1
module Z =
let z = 5
最後,如果外部模組沒有宣告,而且緊接著另一個模組宣告,則新的模組宣告會假設為內部模組,但編譯程式會在第二個模組定義不縮排到第一個模組時發出警告。
// This code produces a warning, but treats Z as a inner module.
module Y =
module Z =
let z = 5
若要消除警告,請縮排內部模組。
module Y =
module Z =
let z = 5
如果您希望檔案中的所有程式代碼都位於單一外部模組中,而且想要內部模組,外部模組不需要等號,而且宣告,包括任何內部模組宣告,都不會縮排在外部模組中。 內部模組宣告內的宣告必須縮排。 下列程式代碼顯示此案例。
// 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
遞歸模組
F# 4.1 引進模組的概念,可讓所有自主程式代碼相互遞歸。 這是透過 module rec來完成。
module rec使用 可減輕無法撰寫類型與模組之間相互引用程式代碼的一些痛苦。 以下是下列範例:
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 Orientation = orientation with get, set
member val Sides: PeelState list = [ Unpeeled; Unpeeled; Unpeeled; Unpeeled ] with get, set
member self.IsPeeled =
self.Sides |> List.forall ((=) Peeled)
member self.Peel() =
BananaHelpers.peel self
|> fun peeledSides -> self.Sides <- peeledSides
member self.SqueezeJuiceOut() =
raise (DontSqueezeTheBananaException self)
module BananaHelpers =
let peel (banana: 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)
banana |> flip |> peelSides
請注意,例外狀況 DontSqueezeTheBananaException 和 類別 Banana 都彼此參考。 此外,模組 BananaHelpers 和類別 Banana 也會彼此參考。 如果您從RecursiveModule模組中移除 rec 關鍵詞,則無法在 F# 中表示。
在具有 F# 4.1 的命名空間 中,也可以使用這項功能。