(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
make-gen-stateprocedureCreate a generator state (internal).
generator?procedureCheck if obj is a generator.
(generator? (generator (yield 1))) ; => #t
(generator? '(1 2 3)) ; => #fgenerator-done?procedureCheck 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) ; => #tyield-valueprocedureYield a value and capture continuation (internal).
make-generatorprocedureCreate 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) ; => 2generatorsyntaxCreate 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)yieldsyntaxYield 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 #fgenerator-nextprocedureGet 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)generator->listprocedureCollect 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)generator-takeprocedureTake 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-mapprocedureCreate 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)generator-filterprocedureCreate 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)generator-foldprocedureReduce 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)list->generatorprocedureCreate a generator from a list.
Yields each element of the list in order.
(generator->list (list->generator '(a b c))) ; => (a b c)range-generatorprocedureCreate 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)infinite-generatorprocedureCreate 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)make-schedulerprocedureCreate 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 interleavedscheduler-spawnprocedureAdd a generator task to the scheduler.
The task should be a generator that yields to allow other tasks to run.
scheduler-run-allprocedureRun 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-runprocedureRun scheduler for n steps.
Each step advances one generator to its next yield point. Returns the list of finished generators so far.