Módulos

No contexto de F#, um módulo é um agrupamento de códigos F#, como valores, tipos e valores de função, em um programa em F#. O agrupamento de código em módulos ajuda a manter junto o código relacionado e ajuda a evitar conflitos de nome em seu programa.

Sintaxe

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

Comentários

Um módulo F# é um agrupamento de constructos de código F#, como tipos, valores, valores de função e códigos em associações do. Ele é implementado como uma classe de CLR (Common Language Runtime) que possui somente membros estáticos. Existem dois tipos de declarações de módulo, dependendo da inclusão ou não de todo o arquivo no módulo: uma declaração de módulo de nível superior e uma declaração de módulo local. Uma declaração de módulo de nível superior inclui o arquivo inteiro no módulo. Uma declaração de módulo de nível superior só pode aparecer como a primeira declaração em um arquivo.

Na sintaxe da declaração do módulo de nível superior, o namespace qualificado opcional é a sequência de nomes de namespace aninhados que contém o módulo. O namespace qualificado não precisa ser declarado previamente.

Não é necessário recuar declarações em um módulo de nível superior. É necessário recuar todas as declarações em módulos locais. Em uma declaração de módulo local, somente as declarações que são recuadas sob essa declaração de módulo fazem parte do módulo.

Se um arquivo de código não começar com uma declaração de módulo de nível superior ou uma declaração de namespace, todo o conteúdo do arquivo, incluindo quaisquer módulos locais, será transformado em parte de um módulo de nível superior criado implicitamente com o mesmo nome do arquivo, sem a extensão e com a primeira letra convertida para maiúscula. Por exemplo, considere o arquivo a seguir.

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

Ele seria compilado como se fosse escrito desta maneira:

module Program
let x = 40

Se você tiver diversos módulos em um arquivo, será necessário usar uma declaração de módulo local para cada módulo. Se um namespace delimitador for declarado, esses módulos farão parte dele. Se um namespace delimitador não for declarado, os módulos se tornarão parte do módulo de nível superior criado implicitamente. O exemplo de código a seguir mostra um arquivo de código que contém diversos módulos. O compilador cria implicitamente um módulo de nível superior chamado Multiplemodules, que terá MyModule1 e MyModule2 aninhados nele.

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

Se você tiver diversos arquivos em um projeto ou em uma única compilação ou estiver criando uma biblioteca, deverá incluir uma declaração de namespace ou de módulo na parte superior do arquivo. O compilador F# só determina um nome de módulo implicitamente quando há somente um arquivo em um projeto ou linha de comando de compilação e você está criando um aplicativo.

O accessibility-modifier pode ser um dos seguintes: public, private ou internal. Para mais informações, consulte Controle de acesso. O padrão é público.

Referência de códigos em módulos

Ao fazer referência a funções, tipos e valores de outro módulo, é necessário usar um nome qualificado ou abrir o módulo. Se você usar um nome qualificado, deverá especificar os namespaces, o módulo e o identificador do elemento de programa desejado. Separe cada parte do caminho qualificado com um ponto (.), conforme a seguir.

Namespace1.Namespace2.ModuleName.Identifier

É possível abrir o módulo ou um ou mais namespaces para simplificar o código. Para saber como abrir namespaces e módulos, confira Declarações de importação: a palavra-chave open.

O exemplo de código a seguir mostra um módulo de nível superior que contém todo o código até o final do arquivo.

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

Para usar esse código de outro arquivo no mesmo projeto, use nomes qualificados ou abra o módulo antes de usar as funções, conforme mostrado nos exemplos a seguir.

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

Módulos aninhados

Os módulos podem ser aninhados. Os módulos internos devem ser recuados até as declarações do módulo externo para indicar que são módulos internos, não módulos novos. Por exemplo, compare os dois exemplos a seguir. O módulo Z é um módulo interno no código a seguir.

module Y =
    let x = 1

    module Z =
        let z = 5

No entanto, o módulo Z é irmão do módulo Y no código a seguir.

module Y =
    let x = 1

module Z =
    let z = 5

O módulo Z também é um módulo irmão no código a seguir, pois não está tão recuado quanto a outras declarações no módulo Y.

module Y =
        let x = 1

    module Z =
        let z = 5

Finalmente, se o módulo externo não tiver declarações e for seguido imediatamente por outra declaração de módulo, a nova declaração será considerada um módulo interno, mas o compilador avisará se a definição do segundo módulo não for mais recuada do que a primeira.

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

Para eliminar o aviso, recue o módulo interno.

module Y =
    module Z =
        let z = 5

Se você deseja que todo o código em um arquivo esteja em um único módulo externo e deseja módulos internos, o módulo externo não exige o sinal de igual e as declarações, incluindo quaisquer declarações de módulo interno, que serão enviadas ao módulo externo não precisam ser recuadas. As declarações dentro de declarações do módulo interno precisam ser recuadas. O código a seguir mostra esse caso.

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

Módulos recursivos

O F# 4.1 introduz a noção de módulos, que permite que todos os códigos contidos sejam mutuamente recursivos. Isso é feito por meio do module rec. O uso de module rec pode ajudar em problemas ao escrever o código mutuamente referencial entre tipos e módulos. Veja o seguinte exemplo desse caso:

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

Observe que a exceção DontSqueezeTheBananaException e a classe Banana fazem referência entre si. Além disso, o módulo BananaHelpers e a classe Banana também se referenciam. Isso não seria possível de expressar em F# se você removesse a palavra-chave rec do módulo RecursiveModule.

Essa funcionalidade também é possível em namespaces com o F# 4.1.

Confira também