F Sharp Programming/Modules and Namespaces

Modules and Namespaces are primarily used for grouping and organizing code.

Defining Modules
No code is required to define a module. If a codefile does not contain a leading  or   declaration, F# code will implicitly place the code in a module, where the name of the module is the same as the file name with the first letter capitalized.

To access code in another module, simply use  notation:. Notice that this notation is similar to the syntax used to access static members—this is not a coincidence. F# modules are compiled as classes which only contain static members, values, and type definitions.

Let's create two files:

DataStructures.fs

Program.fs

This program outputs: StackNode (1,StackNode (2,StackNode (3,EmptyStack))) StackNode (5,  StackNode     (6,StackNode (7,StackNode (8,StackNode (9,StackNode (10,EmptyStack))))))


 * Note: Remember, order of compilation matters in F#. Dependencies must come before dependents, so  comes before   when compiling this program.

Like all modules, we can use the  keyword to give us access to the methods inside a module without fully qualifying the naming of the method. This allows us to revise  as follows:

Submodules
Its very easy to create submodules using the  keyword:

Since the  method is under another module, the fully qualified name of this method is. We can use it as follows:

F# allows us to create a module and a type having the same name, for example the following code is perfectly acceptable:


 * Note: Its possible to nest submodules inside other submodules. However, as a general principle, its best to avoid creating complex module hierarchies. Functional programming libraries tend to be very "flat" with nearly all functionality accessible in the first 2 or 3 levels of a hierarchy. This is in contrast to many other OO languages which encourage programmers to create deeply nested class libraries, where functionality might be buried 8 or 10 levels down the hierarchy.

Extending Types and Modules
F# supports extension methods, which allow programmers to add new static and instance methods to classes and modules without inheriting from them.

Extending a Module

The  module contains several pairs of methods:

has a  member, but does not have a corresponding   function, which includes the index of each sequence element. We add this missing method to the module simply by creating another module with the same name. For example, using fsi:

Extending a Type

The  has many useful methods, but let's say we thought it was missing a few important functions,   and. Since this class is marked as  or , we can't create a derived version of this class. Instead, we create a module with the new methods we want. Here's an example in fsi which demonstrates how to add new static and instance methods to the  class:

Module Signatures
By default, all members in a module are accessible from outside the module. However, a module often contain members which should not be accessible outside itself, such as helper functions. One way to expose only a subset of a module's members is by creating a signature file for that module. (Another way is to apply .Net CLR access modifiers of public, internal, or private to individual declarations).

Signature files have the same name as their corresponding module, but end with a ".fsi" extension (f-sharp interface). Signature files always come before their implementation files, which have a corresponding ".fs" extension. For example:

DataStructures.fsi

DataStructures.fs

Program.fs Since  is not defined in our interface file, the method is marked   and no longer accessible outside of the   module.

Module signatures are useful to building a code library's skeleton, however they have a few caveats. If you want to expose a class, record, or union in a module through a signature, then the signature file must expose all of the objects members, records fields, and union's cases. Additionally, the signature of the function defined in the module and it's corresponding signature in the signature file must match exactly. Unlike OCaml, F# does not allow a function in a module with the generic type  to be restricted to   in the signature file.

Defining Namespaces
A namespace is a hierarchical categorization of modules, classes, and other namespaces. For example, the  namespace groups together all of the collections and data structures in the .NET BCL, whereas the   namespace groups together all classes which provide cryptographic services.

Namespaces are primarily used to avoid name conflicts. For example, let's say we were writing an application incorporated code from several different vendors. If Vendor A and Vendor B both have a class called, and we wrote the code  , how would the compiler know whether which stack we intended to create? Namespaces can eliminate this ambiguity by adding one more layer of grouping to our code.

Code is grouped under a namespace using the  keyword:

DataStructures.fsi

DataStructures.fs

Program.fs

Where is the  Module?

You may have expected the code in  above to open   rather than. According to the F# spec, F# treats anonymous implementation files (which are files without a leading  or   declaration) by putting all code in an implicit module which matches the code's filename. Since we have a leading  declaration, F# does not create the implicit module.

.NET does not permit users to create functions or values outside of classes or modules. As a consequence, we cannot write the following code:

If we prefer to have a module called, we can write this:

Or equivalently, we define a module and place it a namespace simultaneously using:

Adding to Namespace from Multiple Files
Unlike modules and classes, any file can contribute to a namespace. For example:

DataStructures.fs

MoreDataStructures.fs

Since we have a leading namespace declaration in both files, F# does not create any implicit modules. The,  ,  , and   types are all accessible through the   namespace:

Program.fs

Controlling Class and Module Accessibility
Unlike modules, there is no equivalent to a signature file for namespaces. Instead, the visibility of classes and submodules is controlled through standard accessibility modifiers: