Compartir vía


Módulos

En el contexto de F#, un módulo es una agrupación de código F#, como valores, tipos y valores de función, en un programa de F#. La agrupación de código en módulos ayuda a mantener el código relacionado juntos y ayuda a evitar conflictos de nombres en el programa.

Sintaxis

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

Observaciones

Un módulo de F# es una agrupación de construcciones de código de F#, como tipos, valores, valores de función y código en do enlaces. Se implementa como una clase de Common Language Runtime (CLR) que solo tiene miembros estáticos. Hay dos tipos de declaraciones de módulo, en función de si todo el archivo se incluye en el módulo: una declaración de módulo de nivel superior y una declaración de módulo local. Una declaración de módulo de nivel superior incluye todo el archivo del módulo. Una declaración de módulo de nivel superior solo puede aparecer como la primera declaración de un archivo.

En la sintaxis de la declaración del módulo de nivel superior, el espacio de nombres completo opcional es la secuencia de nombres de espacio de nombres anidados que contiene el módulo. El espacio de nombres completo no tiene que declararse previamente.

No es necesario sangr declaraciones en un módulo de nivel superior. Tiene que sangr todas las declaraciones en módulos locales. En una declaración de módulo local, solo las declaraciones con sangría de esa declaración de módulo forman parte del módulo.

Si un archivo de código no comienza con una declaración de módulo de nivel superior o una declaración de espacio de nombres, todo el contenido del archivo, incluidos los módulos locales, forma parte de un módulo de nivel superior creado implícitamente que tiene el mismo nombre que el archivo, sin la extensión, con la primera letra convertida en mayúsculas. Por ejemplo, considere el siguiente archivo.

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

Este archivo se compilaría como si estuviera escrito de esta manera:

module Program
let x = 40

Si tiene varios módulos en un archivo, debe usar una declaración de módulo local para cada módulo. Si se declara un espacio de nombres envolvente, estos módulos forman parte del espacio de nombres envolvente. Si no se declara un espacio de nombres envolvente, los módulos forman parte del módulo de nivel superior creado implícitamente. En el ejemplo de código siguiente se muestra un archivo de código que contiene varios módulos. El compilador crea implícitamente un módulo de nivel superior denominado Multiplemodulesy MyModule1 y MyModule2 están anidados en ese módulo de nivel superior.

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

Si tiene varios archivos en un proyecto o en una sola compilación, o si va a compilar una biblioteca, debe incluir una declaración de espacio de nombres o una declaración de módulo en la parte superior del archivo. El compilador de F# solo determina un nombre de módulo implícitamente cuando solo hay un archivo en una línea de comandos de compilación o proyecto y va a crear una aplicación.

El modificador de accesibilidad puede ser uno de los siguientes: public, private, internal. Para obtener más información, consulte Control de acceso. El valor predeterminado es public.

Hacer referencia al código en módulos

Al hacer referencia a funciones, tipos y valores de otro módulo, debe usar un nombre completo o abrir el módulo. Si usa un nombre completo, debe especificar los espacios de nombres, el módulo y el identificador del elemento de programa que desee. Se separa cada parte de la ruta de acceso calificada con un punto (.), como se indica a continuación.

Namespace1.Namespace2.ModuleName.Identifier

Puede abrir el módulo o uno o varios de los espacios de nombres para simplificar el código. Para obtener más información sobre cómo abrir espacios de nombres y módulos, vea Import Declarations: The open Keyword.

En el ejemplo de código siguiente se muestra un módulo de nivel superior que contiene todo el código hasta el final del archivo.

module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y

Para usar este código desde otro archivo del mismo proyecto, use nombres completos o abra el módulo antes de usar las funciones, como se muestra en los ejemplos siguientes.

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

Módulos anidados

Los módulos se pueden anidar. Los módulos internos se deben aplicar sangría a las declaraciones de módulo exterior para indicar que son módulos internos, no módulos nuevos. Por ejemplo, compare los dos ejemplos siguientes. El módulo Z es un módulo interno en el código siguiente.

module Y =
    let x = 1

    module Z =
        let z = 5

Pero el módulo es un módulo Z relacionado con el siguiente Y código.

module Y =
    let x = 1

module Z =
    let z = 5

El módulo Z también es un módulo relacionado en el código siguiente, ya que no se aplica sangría a otras declaraciones del módulo Y.

module Y =
        let x = 1

    module Z =
        let z = 5

Por último, si el módulo externo no tiene declaraciones y va seguida inmediatamente de otra declaración de módulo, se supone que la nueva declaración del módulo es un módulo interno, pero el compilador le advertirá si la segunda definición de módulo no se aplica sangría más allá de la primera.

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

Para eliminar la advertencia, sangría al módulo interno.

module Y =
    module Z =
        let z = 5

Si desea que todo el código de un archivo esté en un único módulo externo y desee módulos internos, el módulo externo no requiere el signo igual y las declaraciones, incluidas las declaraciones de módulo interno, que van en el módulo externo no tienen que aplicar sangría. Es necesario aplicar sangría a las declaraciones de módulo interno dentro de las declaraciones del módulo interno. En el código siguiente se muestra este 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

F# 4.1 presenta la noción de módulos que permiten que todo el código contenido sea mutuamente recursivo. Esto se hace a través de module rec. El uso de module rec puede aliviar algunos problemas al no poder escribir código referencial mutuamente entre tipos y módulos. A continuación se muestra un ejemplo de esto:

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

Tenga en cuenta que la excepción DontSqueezeTheBananaException y la clase Banana se refieren entre sí. Además, el módulo BananaHelpers y la clase Banana también hacen referencia entre sí. Esto no sería posible expresar en F# si quitó la rec palabra clave del RecursiveModule módulo.

Esta funcionalidad también es posible en Espacios de nombres con F# 4.1.

Consulte también