模組
在 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) 類別。 模組宣告有兩種類型 (最上層模組宣告和本機模組宣告),視整個檔案是否包含在模組中而定。 最上層模組宣告會在模組中包含整個檔案。 最上層模組宣告只能顯示為檔案中的第一個宣告。
在最上層模組宣告的語法中,選擇性的 qualified-namespace 是包含模組的巢狀命名空間名稱序列。 不需要在之前先宣告限定命名空間。
您不需要縮排最上層模組中的宣告。 您必須縮排本機模組中的所有宣告。 在本機模組宣告中,只有在該模組宣告下縮排的宣告是模組的一部分。
如果程式碼檔案不是以最上層模組宣告或命名空間宣告開頭,則檔案的整個內容 (包括任何本機模組) 會成為隱含建立之最上層模組的一部分 (該模組的名稱與檔案相同,但不含副檔名,且第一個字母會轉換成大寫)。 例如,請考慮下列檔案。
// In the file program.fs.
let x = 40
此檔案會如以下撰寫方式進行編譯:
module Program
let x = 40
如果您在檔案中有多個模組,則必須針對每個模組使用本機模組宣告。 如果宣告封入命名空間,則這些模組會是封入命名空間的一部分。 如果未宣告封入命名空間,則這些模組會成為隱含建立之最上層模組的一部分。 下列程式碼範例顯示包含多個模組的程式碼檔案。 編譯器會隱含建立名為 Multiplemodules
的最上層模組,並將 MyModule1
和 MyModule2
巢狀於該最上層模組。
// 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# 編譯器只會在專案或編譯命令列中僅有一個檔案,且您正在建立應用程式時,隱含決定模組名稱。
accessibility-modifier 可以是下列其中一項: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
但在下列程式碼中,模組 Z
與模組 Y
同層級。
module Y =
let x = 1
module Z =
let z = 5
在下列程式碼中,模組 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 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
請注意,例外狀況 DontSqueezeTheBananaException
和類別 Banana
會彼此參考。 此外,模組 BananaHelpers
和類別 Banana
也會彼此參考。 如果您已從 RecursiveModule
模組中移除 rec
關鍵字,就無法在 F# 中表達。
您也可以使用 F# 4.1 在命名空間中執行這項功能。