F# Guide — Modules: Organizing Code with Namespaces
In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
F# modules are containers for related functions, types, and values that help organize code, control scope, and create hierarchical namespaces for better project structure.
What You'll Learn
- Creating and using modules
- Module vs namespace differences
- Nested modules
- Module Accessibility
- Opening modules
Why It Matters
Modules keep related code together and prevent naming conflicts. Well-organized modules make code discoverable and maintainable. Durga Antivirus Pro organizes its analysis engine across multiple modules.
Real-World Use
Large F# projects use modules for each subsystem: data access, business logic, API controllers, utilities, and configuration.
flowchart LR
A["Modules"] --> B["Definition"]
B --> C["Nesting"]
C --> D["Access Control"]
D --> E["Best Practices"]
A:::current --> B
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b
Defining Modules
// Simple module
module Math
let add x y = x + y
let subtract x y = x - y
let multiply x y = x * y
// Usage: Math.add 5 3
Module with Types
module Shapes
type Shape =
| Circle of float
| Rectangle of float * float
let area shape =
match shape with
| Circle r -> System.Math.PI * r * r
| Rectangle (w, h) -> w * h
let createCircle radius =
if radius <= 0 then None
else Some (Circle radius)
Namespaces vs Modules
// Namespace (container for modules and types)
namespace MyApp.Data
module Database =
let connect connectionString = ...
module Queries =
let getUsers = ...
Nested Modules
module App =
module Config =
let host = "localhost"
let port = 8080
module Services =
let start () = printfn "Starting on %s:%d" Config.host Config.port
// Access: App.Config.host, App.Services.start()
Module Accessibility
module Internal =
// Private to this module
let private helper x = x * 2
// Public
let public process x = helper x + 1
// Internal.process is accessible
// Internal.helper is NOT accessible outside the module
Opening Modules
// Open a module to use its contents without qualification
open System
open Math // Custom Math module from above
let result = add 5 3 // Instead of Math.add 5 3
// Open with alias
module MyList = List
let first = MyList.head [1; 2; 3]
Auto-Open
// Module auto-opens when containing namespace is opened
[<AutoOpen>]
module Utils =
let greet name = sprintf "Hello, %s!" name
// greet is available without opening Utils
Common Mistakes
1. Confusing modules with namespaces
Namespaces contain modules and types. Modules contain values and functions. Understand the hierarchy.
2. Deeply nested modules
Don't nest modules more than 2-3 levels deep. Flat structures are easier to navigate.
3. Opening too many modules
Excessive opens pollute the namespace. Prefer qualified access or targeted opens.
4. Circular dependencies
Modules that reference each other cause compilation errors. Design acyclic dependency graphs.
5. Mixed accessibility
Be intentional about what's public vs private. Hide implementation details.
Practice Questions
1. What is the difference between a module and a namespace? Namespaces organize types and modules hierarchically. Modules organize values, functions, and types within a namespace.
2. How do you control visibility in a module?
Use private keyword on bindings to hide them outside the module. Public is the default.
3. What does [open when the containing namespace is opened.
Challenge: Design a module structure for a web application with configuration, routing, controllers, and data access.
FAQ
{{< faq question="Can modules be split across files?" >}}
Yes, using namespace declarations across files in the same namespace. But a module itself must be in one file.
{{< /faq >}}
{{< faq question="Are modules compiled to classes?" >}} Yes, F# modules are compiled to static classes in IL. Module bindings become static methods and properties. {{< /faq >}}
{{< faq question="Can modules contain types?" >}} Yes, modules can define types that are scoped to the module. Types can also be in namespaces directly. {{< /faq >}}
{{< faq question="What is the module keyword do?" >} It creates a container with its own scope. Contents must be indented and are accessible via ModuleName.member. {{< /faq >}}
{{< faq question="Can I have module-level mutable state?" >}}
Yes, but use sparingly. let mutable counter = 0 at module level creates shared mutable state.
{{< /faq >}}
Mini Project
Organize a calculator application into modules:
module Calculator.Operations
let add x y = x + y
let subtract x y = x - y
let multiply x y = x * y
let divide x y =
if y = 0 then Error "Division by zero"
else Ok (x / y)
module Calculator.Parser
let parse (s: string) =
let parts = s.Split(' ')
match parts with
| [|a; op; b|] -> Ok (int a, op, int b)
| _ -> Error "Invalid format"
module Calculator.Engine
open Calculator.Operations
let calculate (a, op, b) =
match op with
| "+" -> Ok (add a b)
| "-" -> Ok (subtract a b)
| "*" -> Ok (multiply a b)
| "/" -> divide a b
| _ -> Error "Unknown operator"
What's Next
Now that you understand modules, explore advanced collection operations.
| Topic | Description | Link |
|---|---|---|
| F# Collections | Collection operations | {{< ref "15-collections" >}} |
| F# Pipelines | Data Pipelines | {{< ref "16-pipelines" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro