Modules and Namespaces
Prerequisites: Functions
A module groups related values, functions, and types under a name — the main way F# code is organized.
module Temperature =
let freezingC = 0.0
let toFahrenheit c = c * 9.0 / 5.0 + 32.0
let toCelsius f = (f - 32.0) * 5.0 / 9.0
module Format =
let degrees unit value = $"%.1f{value}°{unit}"
printfn "%s" (Temperature.toFahrenheit 100.0 |> Format.degrees "F")
printfn "%s" (Temperature.toCelsius 98.6 |> Format.degrees "C")
open Temperature
printfn "%.1f" (toFahrenheit freezingC)
212.0°F
37.0°C
32.0
- Access members with
Module.name, oropen Moduleto use them directly. - The standard library is organized the same way:
List.map,String.concat, andOption.defaultValueare functions in theList,String, andOptionmodules.
Module per type
A common pattern is a type plus a module with the same name holding its functions:
type Money = { Amount: decimal; Currency: string }
module Money =
let create currency amount = { Amount = amount; Currency = currency }
let add a b =
if a.Currency <> b.Currency then Error "currency mismatch"
else Ok { a with Amount = a.Amount + b.Amount }
let format m = $"{m.Amount:F2} {m.Currency}"
let usd = Money.create "USD"
match Money.add (usd 10m) (usd 2.5m) with
| Ok total -> printfn "%s" (Money.format total)
| Error e -> printfn "%s" e
printfn "%A" (Money.add (usd 1m) (Money.create "EUR" 1m))
12.50 USD
Error "currency mismatch"
Private members
Mark helpers private to hide them from code outside the module:
module Password =
let private minLength = 8
let private hasDigit (s: string) = s |> Seq.exists System.Char.IsDigit
let isStrong (s: string) = s.Length >= minLength && hasDigit s
printfn "%b %b" (Password.isStrong "hunter2") (Password.isStrong "correct-horse-7")
false true
Files, modules, and namespaces in a project
In a project, each .fs file usually starts with a declaration:
// illustrative: Shipping.fs — a top-level module
module MyShop.Shipping
let cost weightKg = if weightKg < 1.0 then 4.99m else 9.99m
// illustrative: Domain.fs — a namespace holding types and modules
namespace MyShop.Domain
type Product = { Sku: string; Price: decimal }
module Product =
let isFree p = p.Price = 0m
module A.B.Cat the top makes the whole file one module.namespace A.Bholds types and modules, but not looseletvalues — namespaces are how .NET organizes libraries, and they're what C# code sees.- A script (
.fsx) is implicitly a module named after the file.
Remember that file order in the .fsproj matters: a file can use
modules only from files listed above it.
RequireQualifiedAccess and AutoOpen
[<RequireQualifiedAccess>]
module Color =
let red = "#ff0000"
[<AutoOpen>]
module Helpers =
let clamp lo hi x = max lo (min hi x)
printfn "%s %d" Color.red (clamp 0 10 42)
#ff0000 10
[<RequireQualifiedAccess>] forces callers to write Color.red (even
after open), avoiding name clashes. [<AutoOpen>] opens a module
automatically when its parent is opened — use it sparingly.