Turmeric provides two related features for lazy, incremental computation:
gen/yield, compiled to C state
machines with no heap allocation per yieldSeq) -- composable combinators built on generators, with
no intermediate allocation when chainedgen formA generator function uses gen in place of a function body and yield to
produce values one at a time. The compiler rewrites each generator into a C
struct plus a _next dispatch function -- no continuations, no heap alloc per
step.
(defn integers-from [start :int] : (Generator :int)
(gen []
(let [^mut i start]
(while true
(yield i)
(set! i (+ i 1))))))
(defn range-gen [lo :int hi :int] : (Generator :int)
(gen []
(let [^mut i lo]
(while (< i hi)
(yield i)
(set! i (+ i 1))))))
Use gen-next to advance the generator. It returns a ptr<void> -- non-NULL
when a value was yielded, NULL when exhausted. Use the gen.tur helpers:
(load "stdlib/gen.tur")
(let [g (range-gen 0 5)]
(while (gen-some? (let [v (gen-next g)]
(when (gen-some? v)
(println (gen-unwrap v)))
v))))
;; gen-for-each macro -- preferred for side effects
(gen-for-each (range-gen 0 5)
(fn [x] (println x)))
; prints 0 1 2 3 4
gen-collect -- materialise to array(let [arr (gen-collect (range-gen 1 6))]
(println (gen-arr-len arr)) ; 5
(println (gen-arr-get arr 2))) ; 3
gen-nth -- nth yielded value(gen-nth (range-gen 10 20) 3) ; => 13 (0-indexed)
yield* -- re-yield from an inner generator(defn concat-gen [g1 g2] : (Generator :int)
(gen []
(yield* g1)
(yield* g2)))
gen bodies| Form | Supported |
|---|---|
while with yield inside |
Yes |
if / cond with yield in branches |
Yes |
let bindings that span a yield point |
Yes (promoted to struct field) |
yield inside match arms |
No (v1 limitation) |
| Recursive generators | No (v1 limitation) |
The compiler emits a struct per gen body and a _next function with a
switch on the state tag. Each yield saves state and returns. This means:
next callSeq)The Seq type wraps a generator-producing thunk. Combinators chain generators
without building intermediate collections.
(import seq/core :refer [seq-from-vec seq-of empty-seq])
(import seq/builders :refer [seq/range seq/repeat])
(import seq/transform :refer [seq/map seq/filter seq/take seq/drop])
(import seq/combine :refer [seq/zip seq/concat])
(import seq/consume :refer [seq/into-vec seq/foldl seq/for-each seq/first])
;; From a range [lo, hi)
(seq/range 0 10)
;; From a range with step
(seq/range-step 0 20 2) ; 0 2 4 6 8 10 12 14 16 18
;; Wrap a vec
(seq-from-vec [1 2 3 4 5])
;; Single-element sequence
(seq-of 42)
;; Infinite repetition
(seq/repeat "hello")
;; Infinite thunk calls
(seq/repeatedly (fn [] (rand-int 100)))
;; x, f(x), f(f(x)), ...
(seq/iterate 1 (fn [x] (* x 2))) ; 1 2 4 8 16 ...
;; Cycle a finite sequence infinitely
(seq/cycle (seq/range 0 3)) ; 0 1 2 0 1 2 0 1 2 ...
All transformations are lazy -- no work is done until a consumer drives the sequence:
;; map
(seq/map (fn [x] (* x x)) (seq/range 1 6))
; lazy: 1 4 9 16 25
;; filter
(seq/filter even? (seq/range 0 10))
; lazy: 0 2 4 6 8
;; take / drop
(seq/take 3 (seq/repeat 7)) ; lazy: 7 7 7
(seq/drop 2 (seq/range 0 5)) ; lazy: 2 3 4
;; take-while / drop-while
(seq/take-while (fn [x] (< x 5)) (seq/range 0 100))
; lazy: 0 1 2 3 4
;; map-indexed: receive (index value) pairs
(seq/map-indexed (fn [i x] (pair i x)) (seq/range 10 13))
; lazy: (0 10) (1 11) (2 12)
;; filter-map: map + filter in one pass (None values dropped)
(seq/filter-map (fn [x] (if (even? x) (some (* x 10)) (none)))
(seq/range 0 5))
; lazy: 0 20 40
;; flat-map
(seq/flat-map (fn [x] (seq/range 0 x)) (seq/range 1 4))
; lazy: 0 0 1 0 1 2
;; flatten
(seq/flatten (seq-from-vec [(seq/range 0 2) (seq/range 5 7)]))
; lazy: 0 1 5 6
;; concat two sequences
(seq/concat (seq/range 0 3) (seq/range 10 13))
; lazy: 0 1 2 10 11 12
;; zip two sequences (stops at shorter)
(seq/zip (seq/range 0 3) (seq/range 10 13))
; lazy: (0 10) (1 11) (2 12)
;; zip-with a combining function
(seq/zip-with + (seq/range 0 3) (seq/range 10 13))
; lazy: 10 12 14
;; interleave: alternate elements
(seq/interleave (seq/range 0 3) (seq/range 10 13))
; lazy: 0 10 1 11 2 12
;; collect to vec
(seq/into-vec (seq/range 0 5)) ; => [0 1 2 3 4]
;; collect to cons list
(seq/into-list (seq/range 0 3)) ; => (0 1 2)
;; nth element (0-indexed)
(seq/nth 2 (seq/range 10 20)) ; => (some 12)
;; first element
(seq/first (seq/filter even? (seq/range 1 10))) ; => (some 2)
;; count elements
(seq/count (seq/range 0 100)) ; => 100
;; foldl: left fold with accumulator
(seq/foldl 0 + (seq/range 1 6)) ; => 15
;; reduce: fold with first element as seed
(seq/reduce + (seq/range 1 6)) ; => (some 15)
;; for-each: side effects
(seq/for-each println (seq/range 0 3)) ; prints 0 1 2
;; short-circuit consumers
(seq/any? even? (seq/range 1 10)) ; => true (stops at 2)
(seq/all? even? (seq/range 0 6 2)) ; => true (checks 0 2 4)
(seq/find even? (seq/range 1 10)) ; => (some 2)
(seq/find-index even? (seq/range 1 10)) ; => (some 1)
->> (pipeline style)(->> (seq/range 0 1000)
(seq/filter even?)
(seq/map (fn [x] (* x x)))
(seq/take-while (fn [x] (< x 10000)))
(seq/foldl 0 +))
; => 0 + 4 + 16 + 36 + ... + 9604
No intermediate vecs are allocated; the chain compiles to nested state-machine dispatch.
Range TypesRange is a first-class value type representing a continuous interval with
inclusive, exclusive, or unbounded endpoints. It is not itself a sequence, but
integer ranges can be converted to Seq.
(import range :refer [closed-range open-range closed-open-range
range-contains? range-span seq/from-range])
| Function | Interval |
|---|---|
(closed-range lo hi) |
[lo, hi] -- both inclusive |
(open-range lo hi) |
(lo, hi) -- both exclusive |
(closed-open-range lo hi) |
[lo, hi) -- inclusive lower, exclusive upper |
(open-closed-range lo hi) |
(lo, hi] -- exclusive lower, inclusive upper |
(at-least-range lo) |
[lo, +inf) |
(greater-than-range lo) |
(lo, +inf) |
(at-most-range hi) |
(-inf, hi] |
(less-than-range hi) |
(-inf, hi) |
(singleton-range v) |
[v, v] |
(unbounded-range) |
(-inf, +inf) |
(range-contains? (closed-range 1 10) 5) ; => true
(range-contains? (open-range 1 10) 10) ; => false
(range-encloses? (closed-range 0 10) (closed-range 2 8)) ; => true
(range-overlaps? (closed-range 0 5) (closed-range 3 8)) ; => true
(range-connected? (closed-open-range 0 5) (closed-range 5 10)) ; => true
(empty-range? (open-range 5 5)) ; => true
(singleton-range? (closed-range 7 7)) ; => true
(bounded-range? (at-least-range 0)) ; => false
;; Convex hull of two ranges
(range-span (closed-range 0 3) (closed-range 7 10))
; => [0, 10]
;; Gap between non-overlapping ranges
(range-gap (closed-open-range 0 5) (closed-range 7 10))
; => [5, 7)
;; Intersection of overlapping ranges
(range-intersection (closed-range 0 8) (closed-range 5 10))
; => [5, 8]
For discrete (integer) ranges, use seq/from-range or seq/from-range-step:
;; [1, 5] => (Seq 1 2 3 4 5)
(seq/into-vec (seq/from-range (closed-range 1 5)))
; => [1 2 3 4 5]
;; [0, 10) with step 2 => (Seq 0 2 4 6 8)
(seq/into-vec (seq/from-range-step 2 (closed-open-range 0 10)))
; => [0 2 4 6 8]
stdlib/gen.tur API Reference| Symbol | Description |
|---|---|
gen-some? |
True if gen-next returned a value (non-NULL) |
gen-unwrap |
Extract the :int value from a non-NULL gen-next result |
gen-none |
Return NULL to represent no value |
gen-arr-new |
Allocate an empty growable array |
gen-arr-push! |
Append a value to a gen array |
gen-arr-len |
Number of elements in a gen array |
gen-arr-get |
Element at index (0-based) |
gen-collect |
Macro: drive a generator, collect all values into a gen array |
gen-for-each |
Macro: drive a generator, call f on each value |
gen-nth |
Macro: return the nth value from a generator, or gen-none |
yield* |
Macro: re-yield every value from an inner generator |
stdlib/seq/ Module Reference| Module | Exported symbols |
|---|---|
seq/core |
seq-iter, seq-of, empty-seq, seq-from-vec, seq-from-list |
seq/builders |
seq/range, seq/range-step, seq/repeat, seq/repeatedly, seq/cycle, seq/iterate, seq/unfold |
seq/transform |
seq/map, seq/filter, seq/take, seq/drop, seq/take-while, seq/drop-while, seq/map-indexed, seq/filter-map, seq/flat-map, seq/flatten |
seq/combine |
seq/concat, seq/chain, seq/zip, seq/zip-with, seq/interleave |
seq/consume |
seq/into-vec, seq/into-list, seq/nth, seq/first, seq/last, seq/count, seq/reduce, seq/foldl, seq/for-each, seq/any?, seq/all?, seq/find, seq/find-index |
stdlib/range.tur API Reference| Function | Description |
|---|---|
closed-range / open-range / closed-open-range / open-closed-range |
Bounded constructors |
at-least-range / greater-than-range |
Unbounded above |
at-most-range / less-than-range |
Unbounded below |
singleton-range / unbounded-range |
Single value / no bounds |
range-contains? |
Test membership |
range-encloses? |
Test subset |
range-overlaps? |
Test nonempty intersection |
range-connected? |
Test adjacency (touching counts) |
bounded-range? / bounded-above? / bounded-below? |
Boundedness tests |
unbounded-above? / unbounded-below? |
Unboundedness tests |
empty-range? / nonempty-range? / singleton-range? |
Cardinality |
range-span |
Convex hull of two ranges |
range-gap |
Interval between non-overlapping ranges |
range-intersection |
Largest range within both |
seq/from-range |
Integer range to Seq (step 1) |
seq/from-range-step |
Integer range to Seq with custom step |