Редагувати

Поділитися через


Namespaces (F#)

A namespace lets you organize code into areas of related functionality by enabling you to attach a name to a grouping of F# program elements. Namespaces are typically top-level elements in F# files.

Syntax

namespace [rec] [parent-namespaces.]identifier

Remarks

If you want to put code in a namespace, the first declaration in the file must declare the namespace. The contents of the entire file then become part of the namespace, provided no other namespaces declaration exists further in the file. If that is the case, then all code up until the next namespace declaration is considered to be within the first namespace.

Namespaces cannot directly contain values and functions. Instead, values and functions must be included in modules, and modules are included in namespaces. Namespaces can contain types and modules.

XML doc comments can be declared above a namespace, but they're ignored. Compiler directives can also be declared above a namespace.

Namespaces can be declared explicitly with the namespace keyword, or implicitly when declaring a module. To declare a namespace explicitly, use the namespace keyword followed by the namespace name. The following example shows a code file that declares a namespace Widgets with a type and a module included in that namespace.

namespace Widgets

type MyWidget1 =
    member this.WidgetName = "Widget1"

module WidgetsModule =
    let widgetName = "Widget2"

If the entire contents of the file are in one module, you can also declare namespaces implicitly by using the module keyword and providing the new namespace name in the fully qualified module name. The following example shows a code file that declares a namespace Widgets and a module WidgetsModule, which contains a function.

module Widgets.WidgetModule

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

The following code is equivalent to the preceding code, but the module is a local module declaration. In that case, the namespace must appear on its own line.

namespace Widgets

module WidgetModule =

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

If more than one module is required in the same file in one or more namespaces, you must use local module declarations. When you use local module declarations, you cannot use the qualified namespace in the module declarations. The following code shows a file that has a namespace declaration and two local module declarations. In this case, the modules are contained directly in the namespace; there is no implicitly created module that has the same name as the file. Any other code in the file, such as a do binding, is in the namespace but not in the inner modules, so you need to qualify the module member widgetFunction by using the module name.

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

The output of this example is as follows.

Module1 10 20
Module2 5 6

For more information, see Modules.

Nested Namespaces

When you create a nested namespace, you must fully qualify it. Otherwise, you create a new top-level namespace. Indentation is ignored in namespace declarations.

The following example shows how to declare a nested namespace.

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 in Files and Assemblies

Namespaces can span multiple files in a single project or compilation. The term namespace fragment describes the part of a namespace that is included in one file. Namespaces can also span multiple assemblies. For example, the System namespace includes the whole .NET Framework, which spans many assemblies and contains many nested namespaces.

Global Namespace

You use the predefined namespace global to put names in the .NET top-level namespace.

namespace global

type SomeType() =
    member this.SomeMember = 0

You can also use global to reference the top-level .NET namespace, for example, to resolve name conflicts with other namespaces.

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

Recursive namespaces

Namespaces can also be declared as recursive to allow for all contained code to be mutually recursive. This is done via namespace rec. Use of namespace rec can alleviate some pains in not being able to write mutually referential code between types and modules. The following is an example of this:

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

Note that the exception DontSqueezeTheBananaException and the class Banana both refer to each other. Additionally, the module BananaHelpers and the class Banana also refer to each other. This wouldn't be possible to express in F# if you removed the rec keyword from the MutualReferences namespace.

This feature is also available for top-level Modules.

See also