Chapter 6: Modules
Modules let you organize code into reusable libraries with explicit interfaces.
Basic Module Structure
A module is defined with define-library:
(define-library (myapp utils)
(export helper-function
useful-constant)
(begin
(define useful-constant 42)
(define (helper-function x)
(* x useful-constant))))Key parts:
- Library name:
(myapp utils)— a list of symbols - Exports: What's visible to importers
- Begin block: The actual code
Importing Modules
Use import to bring in other modules:
(import (myapp utils))
(helper-function 2) ; => 84
useful-constant ; => 42Import Forms
Import specific bindings:
(import (only (myapp utils) helper-function))Exclude specific bindings:
(import (except (myapp utils) useful-constant))Rename bindings:
(import (rename (myapp utils)
(helper-function helper)))
(helper 2) ; => 84Add a prefix:
(import (prefix (myapp utils) utils:))
(utils:helper-function 2) ; => 84File Organization
Each library lives in its own file. The library name maps to a file path:
(myapp utils) -> myapp/utils.sgl (myapp data models) -> myapp/data/models.sgl
Project structure example:
my-project/
├── main.sgl
└── myapp/
├── utils.sgl
├── config.sgl
└── data/
└── models.sglA Complete Example
Let's create a small math library.
File: mathlib/basic.sgl
(define-library (mathlib basic)
(export square
cube
average)
(begin
(define (square x)
(* x x))
(define (cube x)
(* x x x))
(define (average . nums)
(if (null? nums)
0
(/ (fold-left + 0 nums)
(length nums))))))File: mathlib/geometry.sgl
(define-library (mathlib geometry)
(import (mathlib basic))
(export circle-area
circle-circumference
distance)
(begin
(define pi 3.14159265359)
(define (circle-area radius)
(* pi (square radius)))
(define (circle-circumference radius)
(* 2 pi radius))
(define (distance x1 y1 x2 y2)
(sqrt (+ (square (- x2 x1))
(square (- y2 y1)))))))File: main.sgl
(import (mathlib basic)
(mathlib geometry))
(display "Area of circle with radius 5: ")
(display (circle-area 5))
(newline)
(display "Distance from (0,0) to (3,4): ")
(display (distance 0 0 3 4))
(newline)Running the Example
To run main.sgl, use the -L flag to tell Sigil where to find your modules:
sigil eval -L . -f main.sglThe -L . adds the current directory to the module search path, allowing Sigil to find mathlib/basic.sgl and mathlib/geometry.sgl.
You should see:
Area of circle with radius 5: 78.5398 Distance from (0,0) to (3,4): 5
Note: Without -L ., you'll get an error like library could not be loaded: (mathlib basic) because Sigil doesn't know where to look for your modules.
Internal Definitions
Code not in the export list is private:
(define-library (myapp internal)
(export public-function)
(begin
;; This is private
(define (internal-helper x)
(* x 2))
;; This is public
(define (public-function x)
(internal-helper (+ x 1)))))Users can only access public-function.
Re-exporting
Combine multiple modules into one interface:
(define-library (mathlib)
(import (mathlib basic)
(mathlib geometry))
(export square cube average
circle-area circle-circumference distance))Now users can import just (mathlib) to get everything.
Standard Library Modules
Sigil's standard library is organized as modules:
| Module | Purpose |
|---|---|
(sigil io) | File I/O |
(sigil fs) | Filesystem operations |
(sigil path) | Path manipulation |
(sigil string) | String utilities |
(sigil process) | Process management |
(sigil socket) | TCP networking |
(sigil json) | JSON handling |
(sigil sxml) | XML/HTML processing |
(sigil time) | Date and time |
(sigil test) | Testing framework |
Example:
(import (sigil io)
(sigil path))
(call-with-input-file "data.txt"
(lambda (port)
(display (read-line port))))Module Loading Order
When you import a module:
- Sigil finds the source file based on the library name
- If not already loaded, it compiles and loads the module
- The module's exports become available
Circular dependencies are not allowed. Design your modules as a directed acyclic graph.
Best Practices
- Small, focused modules: Each module should do one thing well
- Clear interfaces: Export only what users need
- Meaningful names: Library names should describe contents
- Documentation: Add comments describing each export
- Minimal imports: Only import what you use
Practice Exercises
- Create a
(utils string)module with string helper procedures. - Refactor the number guessing game into multiple modules.
- Create a module that depends on your
(utils string)module.
What's Next
Let's explore file and user I/O.