sigildocs

Chapter 5: Control Flow

This chapter covers conditionals, pattern matching, and recursion.

Conditionals

if

The basic conditional expression:

(if condition
    then-expression
    else-expression)

Examples:

(if (> x 0)
    "positive"
    "not positive")

(if (null? lst)
    0
    (+ 1 (length (cdr lst))))

The else clause is optional. If omitted and the condition is false, an unspecified value is returned:

> (if #t "yes")
"yes"

cond

Multiple conditions with cond:

(cond
  ((< n 0) "negative")
  ((= n 0) "zero")
  ((< n 10) "small")
  ((< n 100) "medium")
  (else "large"))

The else clause handles any remaining cases. Each clause is (test body ...).

cond also supports the => syntax:

(cond
  ((find predicate lst) => process-result)
  (else default))

If the test is true, its value is passed to the procedure after =>.

case

Match a value against constants:

(case day
  ((monday tuesday wednesday thursday friday) "weekday")
  ((saturday sunday) "weekend")
  (else "unknown"))

when and unless

One-sided conditionals for side effects:

(when (file-exists? path)
  (println "File found!")
  (process-file path))

(unless (directory? path)
  (error "Expected a directory"))

Pattern Matching with match

Pattern matching is a powerful way to destructure and dispatch on data:

(match value
  (pattern1 body1 ...)
  (pattern2 body2 ...)
  ...)

Matching Lists

> (import (sigil match))
> (define (sum-list lst)
    (match lst
      (() 0)
      ((x) x)
      ((x . rest) (+ x (sum-list rest)))))
> (sum-list '(1 2 3 4))
10

Matching Literals

(define (describe val)
  (match val
    (#t "true")
    (#f "false")
    ('() "empty list")
    (_ "something else")))  ; _ matches anything

Matching Symbols

Quote symbols to match them literally:

(define (handle-command cmd)
  (match cmd
    ('quit (exit))
    ('help (show-help))
    ('status (show-status))
    (_ (println "Unknown command"))))

Binding Variables

Unquoted symbols bind to the matched value:

> (match '(1 2 3)
    ((a b c) (+ a b c)))
6
> (match '(point 3 4)
    (('point x y) (sqrt (+ (* x x) (* y y)))))
5

Complex Patterns

(define (describe-pair p)
  (match p
    ((0 . 0) "origin")
    ((0 . y) (str "on y-axis at " y))
    ((x . 0) (str "on x-axis at " x))
    ((x . y) (str "point at " x "," y))))

Recursion

Scheme favors recursion over iteration. Most loops are written recursively.

Basic Recursion

(define (factorial n)
  (if (<= n 1)
      1
      (* n (factorial (- n 1)))))

Tail Recursion

Tail-recursive procedures reuse the current stack frame:

(define (factorial-tail n)
  (define (loop n acc)
    (if (<= n 1)
        acc
        (loop (- n 1) (* n acc))))
  (loop n 1))

The loop call is in tail position — it's the last thing the function does.

Named let for Loops

A common pattern for iteration:

(define (sum-list lst)
  (let loop ((lst lst) (acc 0))
    (if (null? lst)
        acc
        (loop (cdr lst) (+ acc (car lst))))))

Named let creates a local procedure and calls it immediately.

Processing Lists

Common patterns:

;; Map - transform each element
(define (my-map f lst)
  (if (null? lst)
      '()
      (cons (f (car lst))
            (my-map f (cdr lst)))))

;; Filter - keep matching elements
(define (my-filter pred lst)
  (cond
    ((null? lst) '())
    ((pred (car lst))
     (cons (car lst) (my-filter pred (cdr lst))))
    (else
     (my-filter pred (cdr lst)))))

;; Fold - reduce to single value
(define (my-fold f init lst)
  (if (null? lst)
      init
      (my-fold f (f init (car lst)) (cdr lst))))

These are built-in as map, filter, and fold-left.

Higher-Order Procedures

Procedures that take or return procedures:

> (map (lambda (x) (* x x)) '(1 2 3 4))
(1 4 9 16)
> (filter (lambda (x) (> x 2)) '(1 2 3 4 5))
(3 4 5)

Returning a procedure:

> (define (make-adder n)
    (lambda (x) (+ x n)))
> (define add5 (make-adder 5))
> (add5 10)
15

Practical Example

A simple expression evaluator:

(define (eval-expr expr)
  (match expr
    ;; Numbers evaluate to themselves
    ((? number? n) n)
    ;; Quoted values
    (('quote x) x)
    ;; Binary operations
    ((op a b)
     (let ((va (eval-expr a))
           (vb (eval-expr b)))
       (case op
         ((+) (+ va vb))
         ((-) (- va vb))
         ((*) (* va vb))
         ((/) (/ va vb))
         (else (error "Unknown operator" op)))))))

(eval-expr '(+ 1 (* 2 3)))  ; => 7

Note: The (? predicate pattern) syntax tests a predicate. This may not be available in all Sigil versions — check your implementation.

Practice Exercises

  1. Write a recursive procedure to compute Fibonacci numbers.
  2. Use match to write a procedure that counts the depth of nested lists.
  3. Write a procedure that flattens a nested list into a flat list.

What's Next

Let's learn how to organize code into modules.

Next: Modules →