sigildocs

Modules Guide

This guide covers the Sigil module system in depth.

Library Basics

Modules are defined with define-library:

(define-library (myapp utils)
  (export helper-function another-function)
  (begin
    (define (helper-function x) ...)
    (define (another-function y) ...)))

Note: Basic Scheme forms (define, if, lambda, arithmetic, etc.) are available without imports. Use import to bring in additional modules like (sigil io) or (sigil string).

Library Names

Library names are lists of symbols that form a hierarchical namespace:

(sigil io)           ; Standard library module
(myapp utils)         ; Your application module
(myapp data models)   ; Nested namespace

Exports

Only explicitly exported bindings are visible to importers:

(export
  public-procedure     ; A procedure
  *global-constant*    ; A value
  my-macro)            ; A macro

Private bindings remain internal:

(define-library (example)
  (export public-fn)
  (begin
    (define (private-helper x) (* x 2))
    (define (public-fn x) (private-helper (+ x 1)))))

Import Forms

Basic Import

(import (myapp utils))

All exports become available by their original names.

Selective Imports

Import only specific bindings:

(import (only (myapp utils)
              helper-function))

Import all except specific bindings:

(import (except (myapp utils)
                deprecated-function))

Renaming

Rename bindings to avoid conflicts or improve clarity:

(import (rename (myapp utils)
                (helper-function util-helper)
                (another-function util-other)))

(util-helper 42)  ; Use the renamed binding

Prefixes

Add a prefix to all imported bindings:

(import (prefix (myapp utils) utils:))

(utils:helper-function 42)

Combining Forms

Import forms can be combined:

(import (prefix
          (only (myapp utils) helper-function)
          u:))

(u:helper-function 42)

File Organization

Mapping Names to Files

Library names map to file paths:

Library NameFile Path
(myapp utils)myapp/utils.sgl
(myapp data models)myapp/data/models.sgl
(sigil io)sigil/io.sgl

Search Path

Sigil searches for libraries in:

  1. Current directory
  2. Load path directories (set via environment or command line)
  3. Standard library location

Recommended Structure

my-project/
├── package.sgl          ; Package metadata
├── src/
│   └── myapp/
│       ├── main.sgl     ; Entry point
│       ├── core.sgl     ; Core functionality
│       ├── utils.sgl    ; Utilities
│       └── data/
│           ├── models.sgl   ; Data models
│           └── storage.sgl  ; Persistence
└── test/
    ├── test-core.sgl
    └── test-utils.sgl

Multiple begin Blocks

A library can have multiple begin blocks for organization:

(define-library (myapp utils)
  (export string-helpers list-helpers)

  ;; String utilities
  (begin
    (define (string-helpers ...) ...))

  ;; List utilities
  (begin
    (define (list-helpers ...) ...)))

Conditional Code

Use cond-expand for platform-specific code:

(define-library (myapp platform)
  (export get-home-directory)
  (cond-expand
    (linux
      (begin
        (define (get-home-directory)
          (getenv "HOME"))))
    (windows
      (begin
        (define (get-home-directory)
          (getenv "USERPROFILE"))))
    (else
      (begin
        (define (get-home-directory) #f)))))

Re-exporting

Create facade modules that re-export from multiple sources:

(define-library (myapp)
  (import (myapp core)
          (myapp utils)
          (myapp data))
  (export
    ;; From core
    initialize shutdown
    ;; From utils
    helper-function
    ;; From data
    save-data load-data))

Users can now import just (myapp) to get the public API.

Circular Dependencies

Sigil does not support circular imports. If module A imports module B, then B cannot import A.

Solutions:

  1. Extract shared code: Move common definitions to a third module
  2. Dependency injection: Pass procedures as parameters
  3. Restructure: Rethink module boundaries

Best Practices

1. Single Responsibility

Each module should have one clear purpose:

;; Good: focused modules
(myapp config)     ; Configuration handling
(myapp logging)    ; Logging utilities
(myapp http)       ; HTTP client

;; Avoid: kitchen-sink modules
(myapp utils)      ; Contains everything

2. Minimal Exports

Export only what users need:

;; Good: clean interface
(export
  connect
  disconnect
  send-message)

;; Avoid: exposing internals
(export
  connect disconnect send-message
  internal-buffer retry-count socket-handle)

3. Clear Naming

Use descriptive names that indicate the module's purpose:

(myapp user-authentication)  ; Clear
(myapp ua)                   ; Unclear

4. Documentation

Document public APIs:

(define-library (myapp http)
  (export
   ;; Connect to a URL, returns a connection or #f on failure
   http-connect

   ;; Send a GET request, returns response body as string
   http-get

   ;; Close a connection
   http-close)
  ...)

5. Test Modules Separately

Create corresponding test modules:

myapp/
├── http.sgl
└── test/
    └── test-http.sgl

Compilation

When you use sigil build, modules are automatically compiled to bytecode (.sgb files) which load faster than source.

For standalone applications created with sigil bundle, all modules are bundled into the executable.