sigildocs

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      ; => 42

Import 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)  ; => 84

Add a prefix:

(import (prefix (myapp utils) utils:))
(utils:helper-function 2)  ; => 84

File 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.sgl

A 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.sgl

The -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:

ModulePurpose
(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:

  1. Sigil finds the source file based on the library name
  2. If not already loaded, it compiles and loads the module
  3. The module's exports become available

Circular dependencies are not allowed. Design your modules as a directed acyclic graph.

Best Practices

  1. Small, focused modules: Each module should do one thing well
  2. Clear interfaces: Export only what users need
  3. Meaningful names: Library names should describe contents
  4. Documentation: Add comments describing each export
  5. Minimal imports: Only import what you use

Practice Exercises

  1. Create a (utils string) module with string helper procedures.
  2. Refactor the number guessing game into multiple modules.
  3. Create a module that depends on your (utils string) module.

What's Next

Let's explore file and user I/O.

Next: I/O →