sigildocs

(sigil generators)

(sigil generators) - Generators and Cooperative Scheduling

Lazy iteration using delimited continuations. Generators produce values one at a time on demand, enabling memory-efficient processing of sequences.

Basic Usage

(import (sigil generators))

;; Create a generator that yields values
(define g (generator
            (yield 1)
            (yield 2)
            (yield 3)))

(generator-next g)  ; => 1
(generator-next g)  ; => 2
(generator->list g) ; => (3)

Infinite Sequences

Generators can represent infinite sequences safely:

;; Infinite sequence of squares
(define squares
  (generator
    (let loop ((i 0))
      (yield (* i i))
      (loop (+ i 1)))))

(generator-take squares 5)  ; => (0 1 4 9 16)

Transformations

Chain generators with map, filter, and fold:

(define evens
  (generator-filter even? (range-generator 0 10)))
(generator->list evens)  ; => (0 2 4 6 8)

Exports

Create a generator state (internal).

generator?procedure

Check if obj is a generator.

(generator? (generator (yield 1)))  ; => #t
(generator? '(1 2 3))               ; => #f

Check if a generator is exhausted.

A generator is done when it has yielded all its values and the body has returned.

(define g (generator (yield 1)))
(generator-done? g)   ; => #f
(generator-next g)    ; => 1
(generator-done? g)   ; => #t
yield-valueprocedure

Yield a value and capture continuation (internal).

Create a generator from a thunk.

The thunk should call yield to produce values. When the thunk returns, the generator is exhausted. Prefer using the generator macro for cleaner syntax.

(define g (make-generator
            (lambda ()
              (yield 1)
              (yield 2))))
(generator-next g)  ; => 1
(generator-next g)  ; => 2
generatorsyntax

Create a generator from body expressions.

The body can use yield to produce values lazily. Each call to generator-next resumes execution until the next yield.

(define g (generator
            (yield 'a)
            (yield 'b)
            (yield 'c)))
(generator->list g)  ; => (a b c)
yieldsyntax

Yield a value from within a generator.

Suspends the generator and returns the value to the caller. Execution resumes from this point on the next generator-next. Called with no arguments, yields #f.

(generator
  (yield 1)
  (yield 2)
  (yield))  ; yields #f

Get the next value from a generator.

Advances the generator to the next yield point and returns the yielded value. Returns #f if the generator is exhausted. Note: #f is also returned if the generator yields #f, so use generator-done? to distinguish end-of-sequence from a #f value.

(define g (generator (yield 1) (yield 2)))
(generator-next g)  ; => 1
(generator-next g)  ; => 2
(generator-next g)  ; => #f (exhausted)

Collect all remaining generator values into a list.

Exhausts the generator. For infinite generators, use generator-take instead.

(generator->list (generator (yield 1) (yield 2) (yield 3)))
; => (1 2 3)

Take at most n values from a generator.

Returns a list of up to n values. Safe to use with infinite generators.

(generator-take (range-generator 0 100) 5)  ; => (0 1 2 3 4)
generator-mapprocedure

Create a new generator that applies a function to each value.

Returns a lazy generator - values are transformed on demand.

(define doubled (generator-map (lambda (x) (* x 2))
                               (range-generator 1 5)))
(generator->list doubled)  ; => (2 4 6 8)

Create a new generator that only yields values matching a predicate.

Returns a lazy generator - filtering happens on demand.

(define evens (generator-filter even? (range-generator 0 10)))
(generator->list evens)  ; => (0 2 4 6 8)

Reduce a generator to a single value.

Applies f to the accumulator and each generator value in turn. Exhausts the generator.

(generator-fold + 0 (range-generator 1 6))  ; => 15 (sum of 1..5)

Create a generator from a list.

Yields each element of the list in order.

(generator->list (list->generator '(a b c)))  ; => (a b c)

Create a generator that yields integers in a range.

Yields integers from start (inclusive) to end (exclusive).

(generator->list (range-generator 0 5))  ; => (0 1 2 3 4)
(generator->list (range-generator 5 8))  ; => (5 6 7)

Create an infinite generator from a state machine.

The function f takes the current state and returns a pair (value . next-state). Use with generator-take to avoid infinite loops.

;; Fibonacci sequence
(define fibs (infinite-generator
               (lambda (state)
                 (let ((a (car state)) (b (cdr state)))
                   (cons a (cons b (+ a b)))))
               '(0 . 1)))
(generator-take fibs 8)  ; => (0 1 1 2 3 5 8 13)

Create a cooperative scheduler for running generators.

The scheduler runs multiple generators in round-robin fashion. Each generator yields to give others a chance to run.

(define sched (make-scheduler))
(scheduler-spawn sched (generator (yield 'a) (yield 'b)))
(scheduler-spawn sched (generator (yield 1) (yield 2)))
(scheduler-run-all sched)  ; runs both interleaved

Add a generator task to the scheduler.

The task should be a generator that yields to allow other tasks to run.

Run scheduler until all tasks complete.

Returns the list of finished generators.

(define sched (make-scheduler))
(scheduler-spawn sched (generator (yield 1) (yield 2)))
(scheduler-run-all sched)
scheduler-runprocedure

Run scheduler for n steps.

Each step advances one generator to its next yield point. Returns the list of finished generators so far.