• Статья
  • Чтение занимает 2 мин

В контексте 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 привязках. Он реализуется как класс среды CLR, имеющий только статические члены. Существует два типа объявлений модулей в зависимости от того, включен ли в модуль весь файл: объявление модуля верхнего уровня и объявление локального модуля. Объявление модуля верхнего уровня включает в себя весь файл в модуле. Объявление модуля верхнего уровня может использоваться только в качестве первого объявления в файле.

В синтаксисе объявления модуля верхнего уровня необязательное полное-пространство имен — это последовательность вложенных имен пространств имен, содержащих модуль. Полное имя пространства имен не обязательно должно быть объявлено ранее.

Добавлять отступы в модуль верхнего уровня не требуется. Необходимо задать отступ для всех объявлений в локальных модулях. В объявлении локального модуля только объявления, имеющие отступ под этим объявлением модуля, являются частью модуля.

Если файл кода не начинается с объявления модуля верхнего уровня или объявления пространства имен, все содержимое файла, включая все локальные модули, станет частью неявно созданного модуля верхнего уровня, который имеет то же имя, что и файл, без расширения с первой буквой, преобразованной в верхний регистр. Например, рассмотрим следующий файл.

// 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 может быть один из следующих: public , private , internal . Дополнительные сведения см. в статье Управление доступом. Значение по умолчанию: public.

Ссылка на код в модулях

При ссылке на функции, типы и значения из другого модуля необходимо либо использовать полное имя, либо открыть модуль. При использовании полного имени необходимо указать пространства имен, модуль и идентификатор нужного элемента программы. Каждая часть полного пути разделяются точкой (.), как показано ниже.

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 также ссылаются друг на друга. Если вы удалили rec ключевое слово из модуля, это невозможно будет сделать в F # RecursiveModule .

Эта возможность также возможна в пространствах имен с F # 4,1.

См. также

  • Справочник по языку F#
  • Пространства имен
  • F # RFC FS-1009-разрешить взаимно ссылочные типы и модули в более крупных областях внутри файлов