Skip to content
learn

6. Modules, namespaces and project organization

A namespace gives .NET types a qualified name. A module groups values, functions and types. C# developers often reach for a static class where F# uses a module.

Official references: namespaces, modules, access control and signature files.

From l06_modules.fsx:

module Music =
module Pitch =
let private normalize pitchClass = ((pitchClass % 12) + 12) % 12
let name pitchClass =
[| "C"; "C#"; "D"; "Eb"; "E"; "F"; "F#"; "G"; "Ab"; "A"; "Bb"; "B" |]
|> Array.item (normalize pitchClass)

normalize is visible only inside Pitch; Music.Pitch.name is public. internal exposes a member to the current assembly but not to consumers. Use the narrowest visibility that supports tests through public behavior.

namespace GuitarAlchemist.Theory
module Pitch =
let name pitchClass = ...
type Chord(root: int) =
member _.Root = root

A namespace cannot directly contain let bindings; put them in a module. A file can begin with module GuitarAlchemist.Theory.Pitch when the entire file is one module.

F# compiles project files top to bottom. A file may use definitions from files listed before it, never after it.

<ItemGroup>
<Compile Include="Domain.fs" />
<Compile Include="Parser.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

This makes dependency direction visible and prevents accidental cycles. Reordering files in the IDE changes the project file and can change whether it compiles.

A .fsi file precedes the matching .fs file and declares the public surface:

Pitch.fsi
module GuitarAlchemist.Theory.Pitch
val name: pitchClass: int -> string

The implementation may contain helpers that consumers cannot see. Signature files are useful at stable library boundaries, but expensive when every internal function changes; do not add them mechanically to every file.

Split the lesson script into Pitch.fs, Chord.fs and Program.fs, list them in dependency order, then add Pitch.fsi exposing only name. Confirm that trying to call normalize from Program.fs fails at compile time.

Continue with errors and Result.