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.