sigildocs

Idioms

Common patterns and best practices for idiomatic Sigil code.

Output

Use print/println with format strings, not display/newline:

;; Idiomatic
(println "Hello, ~a!" name)
(println "Got ~a items" (length items))

;; Avoid (R7RS compatibility only)
(display "Hello, ") (display name) (newline)

Threading Pipelines

Use -> for data transformation pipelines instead of nested calls:

;; Idiomatic - reads top to bottom
(-> data
    (filter positive? _)
    (map square _)
    (take 10 _))

;; Avoid - reads inside out
(take 10 (map square (filter positive? data)))

The _ placeholder marks where the threaded value goes. Use some-> for nil-short-circuiting:

(some-> user
        (dict-ref _ email:)
        validate-email
        send-confirmation)  ; stops if any step returns #f

Pattern Matching

Use match for type/shape dispatch instead of nested cond:

(match cmd
  (('add x y) (+ x y))
  (('quit) (exit 0))
  ((? string? s) (parse-string s))
  (_ (error "Unknown command")))

Use match-lambda for inline match functions:

(map (match-lambda
       ((name . value) (format "~a=~a" name value)))
     alist)

Use match-let for destructuring binds:

(match-let (((x y) point)
            ((w h) size))
  (* (+ x w) (+ y h)))

Polymorphic Collections

Import (sigil seq) for operations that work on lists, vectors, arrays, and dicts:

(import (sigil seq))

(map square '(1 2 3))     ; => (1 4 9)
(map square #(1 2 3))     ; => #(1 4 9)
(filter even? #(1 2 3 4)) ; => #(2 4)
(fold + 0 '(1 2 3 4))     ; => 10

Transducers

For complex transformations, compose transducers:

(import (sigil seq))

(define xform
  (comp (filtering even?)
        (mapping square)
        (taking 5)))

(sequence xform (iota 100))  ; => (0 4 16 36 64)
(into #() xform '(1 2 3 4))  ; => #(4 16)

Function Composition

Import (sigil fn) for point-free style:

(import (sigil fn))

(define add1 (partial + 1))
(define process (pipe string-trim string-upcase))
(filter (complement null?) items)

Higher-Order Functions

Prefer HOFs over manual recursion:

;; Good
(filter even? numbers)
(map string-upcase names)
(fold + 0 values)
(filter-map maybe-transform items)

;; Avoid manual recursion for simple cases

Use named let for complex loops:

(let loop ((items items) (acc '()))
  (cond
    ((null? items) (reverse acc))
    ((valid? (car items))
     (loop (cdr items) (cons (transform (car items)) acc)))
    (else (loop (cdr items) acc))))

Dicts

Prefer dicts over alists for data:

(define config
  #{ host: "localhost"
     port: 8080
     debug: #f })

(dict-ref config port:)           ; => 8080
(dict-set config debug: #t)       ; => new dict
(dict-merge defaults user-config) ; => merged dict

Use dict-get-in for nested access:

(dict-get-in response '(body: data: items:))

Keyword Arguments

Use keywords for optional parameters:

(define (connect host (keys: (port 80) (timeout 30)))
  ...)

(connect "localhost")
(connect "localhost" port: 443 timeout: 60)

Error Handling

Use guard for expected errors:

(guard (e ((file-error? e) #f))
  (read-file-string path))

Use error for unexpected conditions:

(unless user
  (error "User not found: ~a" id))

Boolean Expressions

Use and/or for short-circuit logic:

(or (find-cached key) (compute-value key))
(and (valid? x) (authorized? user) (process x))

Avoid redundant boolean conversion:

;; Good
(not (null? items))

;; Bad
(if (not (null? items)) #t #f)

Multi-List Operations

For iterating multiple lists together, use (sigil list):

(import (sigil list))

(list-map + '(1 2 3) '(10 20 30))  ; => (11 22 33)
(list-fold (lambda (a b acc) (cons (+ a b) acc))
           '() '(1 2) '(10 20))   ; => (22 11)

Module Organization

(define-library (myapp feature)
  (import (sigil string))
  (export public-api)
  (begin
    ;; Private helpers first
    (define (helper x) ...)

    ;; Public API
    (define (public-api x)
      (helper x))))