Basics 20 - Namespaces and Modules

Both namespaces and modules are used for organizing the code in a hierarchical, logical way.

Namespaces

Namespaces can contain:

  1. Nested namespaces

  2. Modules

  3. Types

Modules

Modules are compiled to static classes in .NET. All values and functions which are part of a module are compiled to static members.

Modules can contain:

  1. Sub-modules

  2. Types

  3. Values

  4. Functions

Examples

The Typical Case

This is the most common case, namespace and module are declared.

// Program.fs

// NsName
namespace NsName

// NsName.ModuleName
module ModuleName =
    // NsName.ModuleName.main
    [<EntryPoint>]
    let main args = 0

No Namespace or Module Declaration

When no namespace or module is declared at the top, the file contents are assumed to be part of one module. Module name becomes filename (without .fs) with the first letter capitalized.

Please note, this provision is available only for the last file in the F# project. For all the other files, you need namespace + module declaration or module declaration at the top.

// someCode.fs

// SomeCode.x
let x args = 0

// SomeCode.y
let y args = 0

// SomeCode.Mod
module Mod = 
    // SomeCode.Mod.x
    let x args = 0

    // SomeCode.Mod.y
    let y args = 0

Combining Namespace and Module Declaration

The syntax module <Namespace name>.<Module name> is shorthand for combining namespace and module declaration. When using this syntax, there can be only one top-level module in the file. If you need multiple top-level modules in a file, declare the namespace and modules separately. Also notice no = sign after module <Namespace name>.<Module name>.

// Program.fs

// NsName.ModuleName
module NsName.ModuleName

// NsName.ModuleName.main
[<EntryPoint>]
let main args = 0

Multiple Top-Level Modules in a File

This is an example of having multiple top-level modules in one file.

// Program.fs

// NsName
namespace NsName

// NsName.Module1
module Module1 =
    // NsName.Module1.add
    let add x y = x + y

// NsName.Module2
module Module2 =
    // NsName.Module2.main
    [<EntryPoint>]
    let main args = 0

Multiple Namespaces and Modules in a File

This is an example of having multiple namespaces and modules in one file.

// Program.fs

// NsName1
namespace NsName1

// NsName1.Module1
module Module1 =
    // NsName1.Module1.add
    let add x y = x + y

// NsName2
namespace NsName2

// NsName2.Module2
module Module2 =
    // NsName2.Module2.main
    [<EntryPoint>]
    let main args = 0

Nested Namespaces

This is an example of nested namespaces.

// Program.fs

// NsName1
namespace NsName1

// NsName1.Module1
module Module1 =
    // NsName1.Module1.add
    let add x y = x + y

// NsName1.NsName2
namespace NsName1.NsName2

// NsName1.NsName2.Module2
module Module2 =
    // NsName1.NsName2.Module2.main
    [<EntryPoint>]
    let main args = 0

Sub-Modules

This is an example of creating a sub-module.

// Program.fs

// NsName1
namespace NsName1

// NsName1.Module1
module Module1 =
    // NsName1.Module1.add
    let add x y = x + y

    // NsName1.Module1.Module2
    module Module2 =
        // NsName1.Module1.Module2.main
        [<EntryPoint>]
        let main args = 0

Recurring Modules

The usage of the keyword rec with module declaration allows all the members of the module to be mutually recursive. This is similar to a C# class body.

Won't work:

namespace NsName1

module Module1 =
    let add x y = x + y

    // Will not work as twoConstant isn't known
    let add2 x = x + twoConstant

    let twoConstant = 2

Will work:

namespace NsName1

module rec Module1 =
    let add x y = x + y

    // Will work
    let add2 x = x + twoConstant

    let twoConstant = 2

Opening Namespaces

The keyword open is used to make the members of a namespace available at the file level.

This is without opening System namespace.

namespace NsName1

module Module1 =
    let addPi x = x + System.Math.PI

This is with opening System namespace.

namespace NsName1

open System

module Module1 =
    let addPi x = x + Math.PI

Opening Types

You can open types also with open type <type>. This means all the static members of the given type are available at the file level.

namespace NsName1

open type System.Math

module Module1 =
    let addPi x = x + PI

If you have reached so far, congratulations.

Keep reading!