Modules in F#

Modules allow us to break up source code into multiple files. There are two types of module, a top-level module and local modules.

Note: I’ve omitted any namespaces, but for completeness of this post, a name space would be added as the first line in the module files (if required) as per

namespace MyNamespace

module MyModule =
   let square x = x * x

The top-level module

The top-level module is basically a module which contains all the code for an application and it has slightly different rules regarding it’s layout compared to the local modules (see Modules (F#).

Reproducing a little from the above link we see that a top-level module does not need an = sign at the end of the module name and declarations need not be indented, for example the following is perfectly acceptable in a top-level module but not a local module

module MyModule

let hello = 
   printfn "Hello"

Local Modules

Local modules are basically re-usable files. The usual usage would be to group together related code. Unlike the top-level module, local modules require indented formatting as well as an = after the module name, so the previously define code for a top-level module would look like the following code in a local module

module MyModule =

   let hello =
      printfn "Hello"

The Program/EntryPoint module

By default the code generated by Visual Studio as the starting point for an F# applications looks like

[<EntryPoint>]
let main argv = 
    printfn "%A" argv
    0 // return an integer exit code

But implicitly this will be compiled as

module Program
[<EntryPoint>]
let main argv = 
    printfn "%A" argv
    0 // return an integer exit code

Thus conversely the module name of a top-level module can be omitted if it’s the only file in the application.

Inner Modules

Not only can a file be declared as a module but you can also have modules nested within modules, for example

module GrandParent =
   module Parent =
      module Child =
         let x = 100

Within a top-level module we can define this as

module GrandParent

module Parent =
   module Child =
      let x = 100

in other words the top level module does not need the = sign or the indenting of the Parent module.

How to use a module

So we’ve shown how to declare modules but at some point we need to use them, to achieve this we use the following

open MyModule

If we’d had a namespace we could open the namespace such as open MyNamespace. In such a case where modules were in the namespace we could prefix them on the functions, such as MyModule.square 3. Alternatively we could open the namespace and specific module(s) such as open MyNamespace.MyModule and thus have no need to prefix the functions with the module name.