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# モジュールは、型、値、関数値、 do
バインド内のコードなどの F# コード コンストラクトのグループです。 静的メンバーのみを持つ共通言語ランタイム (CLR) クラスとして実装されます。 モジュール宣言には、ファイル全体がモジュールに含まれているかどうかに応じて、最上位モジュール宣言とローカル モジュール宣言の 2 種類があります。 最上位レベルのモジュール宣言には、モジュール内のファイル全体が含まれます。 最上位レベルのモジュール宣言は、ファイル内の最初の宣言としてのみ使用できます。
最上位レベルのモジュール宣言の構文では、オプションの 修飾名前空間 は、モジュールを含む入れ子になった名前空間名のシーケンスです。 修飾された名前空間を以前に宣言する必要はありません。
最上位モジュールで宣言をインデントする必要はありません。 ローカル モジュール内のすべての宣言をインデントする必要があります。 ローカル モジュール宣言では、そのモジュール宣言の下でインデントされた宣言のみがモジュールの一部です。
コード ファイルが最上位レベルのモジュール宣言または名前空間宣言で始まらない場合、ローカル モジュールを含むファイルの内容全体が、ファイルと同じ名前を持つ暗黙的に作成された最上位モジュールの一部になり、拡張子は付けず、最初の文字は大文字に変換されます。 たとえば、次のファイルを考えてみましょう。
// 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)
プロジェクトまたは 1 回のコンパイルで複数のファイルがある場合、またはライブラリをビルドする場合は、ファイルの先頭に名前空間宣言またはモジュール宣言を含める必要があります。 F# コンパイラは、プロジェクトまたはコンパイル コマンド ラインにファイルが 1 つだけ存在し、アプリケーションを作成している場合にのみ、モジュール名を暗黙的に決定します。
アクセシビリティ修飾子には、public
、private
、internal
のいずれかを指定できます。 詳細については、「 アクセス制御」を参照してください。 既定値はパブリックです。
モジュールでのコードの参照
別のモジュールから関数、型、値を参照する場合は、修飾名を使用するか、モジュールを開く必要があります。 修飾名を使用する場合は、必要なプログラム要素の名前空間、モジュール、および識別子を指定する必要があります。 修飾パスの各部分は、次のようにドット (.) で区切ります。
Namespace1.Namespace2.ModuleName.Identifier
モジュールまたは 1 つ以上の名前空間を開いて、コードを簡略化できます。 名前空間とモジュールを開く方法の詳細については、「 インポート宣言: 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
入れ子になったモジュール
モジュールは入れ子にすることができます。 内部モジュールは、新しいモジュールではなく内部モジュールであることを示すために、外側のモジュール宣言までインデントする必要があります。 たとえば、次の 2 つの例を比較します。 モジュール 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
最後に、外側のモジュールに宣言がなく、その直後に別のモジュール宣言が続く場合、新しいモジュール宣言は内部モジュールであると見なされますが、2 番目のモジュール定義が最初のモジュール定義よりもインデントされていない場合、コンパイラから警告が表示されます。
// 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
ファイル内のすべてのコードを 1 つの外部モジュールに含め、内部モジュールが必要な場合、外側のモジュールは等号を必要とせず、外側のモジュールで行く内部モジュール宣言を含む宣言はインデントする必要はありません。 内部モジュール宣言内の宣言はインデントする必要があります。 次のコードは、このケースを示しています。
// 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 の 名前空間 でも可能です。
こちらも参照ください
.NET