Namespaces (F#)

Um namespace permite organizar códigos em áreas de funcionalidade relacionada, a fim de permitir a anexação de um nome a um agrupamento de elementos de programa em F#. Os namespaces geralmente são elementos de nível superior em arquivos em F#.

Sintaxe

namespace [rec] [parent-namespaces.]identifier

Comentários

Para adicionar códigos em um namespace, a primeira declaração no arquivo deve declarar o namespace. Assim, todo o conteúdo do arquivo se torna parte do namespace, desde que nenhuma outra declaração de namespaces exista no arquivo. Nesse caso, até a próxima declaração de namespace, será considerado que todo o código está dentro do primeiro namespace.

Os namespaces não podem conter diretamente valores e funções. Em vez disso, os valores e as funções devem ser incluídos em módulos que, por sua vez, são incluídos em namespaces. Os namespaces podem conter tipos e módulos.

Comentários da documentação XML podem ser declarados acima de um namespace, mas serão ignorados. As diretivas do compilador também podem ser declaradas acima de um namespace.

Os namespaces podem ser declarados explicitamente com a palavra-chave namespace ou implicitamente por meio da declaração de um módulo. Para declarar um namespace explicitamente, use a palavra-chave namespace seguida pelo nome do namespace. O exemplo a seguir mostra um arquivo de código que declara um namespace Widgets com um tipo e um módulo incluídos.

namespace Widgets

type MyWidget1 =
    member this.WidgetName = "Widget1"

module WidgetsModule =
    let widgetName = "Widget2"

Se todo o conteúdo do arquivo estiver em um módulo, também será possível declarar namespaces implicitamente usando a palavra-chave module e fornecendo o novo nome de namespace no nome do módulo totalmente qualificado. O exemplo a seguir mostra um arquivo de código que declara um namespace Widgets e um móduloWidgetsModule, que contém uma função.

module Widgets.WidgetModule

let widgetFunction x y =
   printfn "%A %A" x y

O código a seguir é equivalente ao código anterior, mas o módulo é uma declaração de módulo local. Nesse caso, o namespace deve aparecer na própria linha.

namespace Widgets

module WidgetModule =

    let widgetFunction x y =
        printfn "%A %A" x y

Se mais de um módulo for necessário no mesmo arquivo em um ou mais namespaces, será necessário usar declarações de módulo local. Ao usar declarações de módulo local, não é possível usar o namespace qualificado nas declarações de módulo. O código a seguir mostra um arquivo que tem uma declaração de namespace e duas declarações de módulo local. Nesse caso, os módulos estão contidos diretamente no namespace. Não há nenhum módulo criado implicitamente que tenha o mesmo nome do arquivo. Qualquer outro código no arquivo, como uma associação do, está no namespace, mas não nos módulos internos, portanto, é necessário qualificar o membro do módulo widgetFunction usando o nome do módulo.

namespace Widgets

module WidgetModule1 =
   let widgetFunction x y =
      printfn "Module1 %A %A" x y
module WidgetModule2 =
   let widgetFunction x y =
      printfn "Module2 %A %A" x y

module useWidgets =

  do
     WidgetModule1.widgetFunction 10 20
     WidgetModule2.widgetFunction 5 6

Veja a seguir a saída deste exemplo.

Module1 10 20
Module2 5 6

Para saber mais, confira Módulos.

Namespaces aninhados

Ao criar um namespace aninhado, é necessário qualificá-lo totalmente. Caso contrário, você cria um namespace de nível superior. O recuo é ignorado nas declarações de namespace.

O exemplo a seguir mostra como declarar um namespace aninhado.

namespace Outer

    // Full name: Outer.MyClass
    type MyClass() =
       member this.X(x) = x + 1

// Fully qualify any nested namespaces.
namespace Outer.Inner

    // Full name: Outer.Inner.MyClass
    type MyClass() =
       member this.Prop1 = "X"

Namespaces em arquivos e assemblies

Os namespaces podem abranger diversos arquivos em um único projeto ou compilação. O termo fragmento de namespace descreve a parte de um namespace que está incluída em um arquivo. Os namespaces também podem abranger diversos assemblies. Por exemplo, o namespace System inclui todo o .NET Framework, que abrange muitos assemblies e contém muitos namespaces aninhados.

Namespace global

Você usa o namespace predefinido global para colocar nomes no namespace de nível superior .NET.

namespace global

type SomeType() =
    member this.SomeMember = 0

Também é possível usar o namespace global para fazer referência ao namespace .NET de nível superior, por exemplo, a fim de resolver conflitos de nome com outros namespaces.

global.System.Console.WriteLine("Hello World!")

Namespaces recursivos

Os namespaces também podem ser declarados como recursivos para permitir que todos os códigos contidos sejam mutuamente recursivos. Isso é feito por meio de namespace rec. O uso de namespace rec pode ajudar em problemas ao escrever o código mutuamente referencial entre tipos e módulos. Veja o seguinte exemplo desse caso:

namespace rec MutualReferences

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 namespace MutualReferences.

Esse recurso também está disponível para módulos de nível superior.

Confira também