Base lazy Library¶
Overview¶
What is Lazy Evaluation?¶
In Cozenage, and in most programming languages, expressions are evaluated eagerly: as soon
as you write (+ 1 2), the interpreter computes 3 immediately. This is the behaviour you
have come to expect. Lazy evaluation inverts this: an expression is wrapped up and set aside,
and its value is only computed at the moment it is actually needed. Until then, it exists as
an unevaluated promise.
The (base lazy) library provides the tools to work with lazy evaluation explicitly. The
centrepiece is a simple contract: delay wraps an expression in a promise, and force
redeems that promise, triggering evaluation and returning the result.
(define p (delay (+ 1 2))) ; Nothing is computed yet. p is a promise.
(force p) ; Now it is computed. => 3
(force p) ; Computed again? No — the result is cached. => 3
This last point is important: a promise is evaluated at most once. Once forced, the result
is memoised — stored inside the promise object — and every subsequent call to force returns
the cached value immediately. This means any side effects inside a delayed expression also
fire at most once:
(define count 0)
(define p (delay (begin (set! count (+ count 1)) count)))
(force p) ; => 1 (count is now 1)
(force p) ; => 1 (count is still 1 — the body did not run again)
Promises Are Not Only for Streams¶
Before discussing streams, it is worth understanding that delay and force are
independently useful tools, not merely scaffolding for streams.
Deferring expensive work you may not need:
(define expensive-result
(delay (some-very-slow-computation)))
(if (simple-check-passes?)
(force expensive-result) ; Only pay the cost if we actually need it.
"skipped")
Sharing a computation across multiple call sites:
Because forcing a promise memoises its result, you can pass a single promise to multiple parts of your program and guarantee the underlying computation runs exactly once, regardless of how many times it is forced:
(define shared (delay (fetch-large-dataset)))
(process-a (force shared)) ; Runs the computation.
(process-b (force shared)) ; Returns the cached result instantly.
Controlling exactly when a side effect fires:
(define log-entry
(delay (begin (write-to-log "event occurred") 'logged)))
; ... do other things ...
(force log-entry) ; The log entry is written precisely here, once.
None of these examples involve streams. Lazy evaluation is the mechanism; streams are one powerful application of that mechanism.
What is a Stream?¶
A stream is a data structure that represents a sequence of values, potentially infinite, where each element beyond the first is computed only when it is needed.
Compare a list and a stream representing the natural numbers:
; A list must be finite — you cannot hold all natural numbers in memory.
(define first-five-nats (list 0 1 2 3 4))
; A stream can be infinite — the tail is a promise, not a value.
(define (integers-from n)
(stream n (integers-from (+ n 1))))
(define nats (integers-from 0)) ; Represents 0, 1, 2, 3, ... forever.
The stream constructor builds a stream node with two parts: an eager head (evaluated
immediately), and a lazy tail (wrapped in a promise, not evaluated until forced). This is
the crucial difference from a list, where both car and cdr are fully realised values.
Accessing elements forces only as much of the stream as is needed:
(head nats) ; => 0 (no promises forced)
(head (tail nats)) ; => 1 (one promise forced)
(at 100 nats) ; => 100 (100 promises forced, then discarded)
The stream itself is never fully realised in memory. At any given moment, only the portion you have accessed exists as computed values.
The Relationship Between Lazy Evaluation and Streams¶
A useful way to think about this:
delayandforceare to streams whatcons,car, andcdrare to lists — the primitive building blocks, each useful on their own, but also the foundation for something larger.The
streamconstructor is essentially justconswhere the tail is automaticallydelayed for you.headandtailare essentiallycarandcdrwheretailautomaticallyforces the promise.
Streams are lazy evaluation applied systematically to sequences. Once you understand
delay and force, streams follow naturally.
Special Forms vs Procedures: A Note on This Library¶
Most Cozenage libraries export only procedures — functions you call like (sqrt 4) or
(string-length "hello"). The (base lazy) library is unique in that it also exports
special forms (syntax): delay, delay-force, and stream.
The distinction matters. A procedure evaluates all its arguments before the function body
runs. A special form controls evaluation itself — it can choose not to evaluate an argument,
or to evaluate it later. This is precisely what delay must do: if (delay expr) were a
procedure, expr would be evaluated before delay ever had a chance to defer it.
; If delay were a procedure, this would loop forever:
(define ones (delay (stream 1 ones))) ; delay must NOT evaluate its argument eagerly.
Because these special forms must intercept evaluation before it happens, they cannot be
implemented as a library loaded at runtime in the usual way. The type infrastructure
(CELL_PROMISE, CELL_STREAM) and the special form dispatch are wired into the core
interpreter; (import (base lazy)) activates them and adds the accompanying procedures to
the environment.
Working Safely with Infinite Streams¶
Infinite streams require some care. Certain operations are lazy — they return a new stream without traversing the input — and are safe to apply to infinite streams:
(collect (lambda (n) (* n n)) nats) ; Safe — returns a new infinite stream.
(select even? nats) ; Safe — returns a new infinite stream.
(weave nats nats) ; Safe — returns a new infinite stream.
Other operations are eager consumers — they must traverse the stream to produce a result —
and will diverge (loop forever) if applied to an infinite stream without first bounding it
with take:
(reduce + 0 nats) ; Diverges — never terminates.
(reduce + 0 (take 10 nats)) ; Safe — sums the first 10 elements. => 45
As a rule: if an operation returns a stream, it is lazy and safe. If it returns a plain value, it is eager and requires a finite input.
A Complete Example¶
To illustrate how the pieces fit together, here is a small program that computes the first ten squared even numbers using a pipeline of stream operations:
(import (base lazy))
; An infinite stream of natural numbers, built with iterate.
(define nats (iterate (lambda (n) (+ n 1)) 0))
; Filter to evens, then square each one — both operations are lazy.
(define squared-evens
(collect (lambda (n) (* n n))
(select even? nats)))
; Only now do we force evaluation, by taking the first 10.
(take 10 squared-evens)
; => (0 4 16 36 64 100 144 196 256 324)
At no point is the full stream of naturals, evens, or squared evens realised in memory.
Each call to take drives exactly as much evaluation as is needed, and no more.
Special Forms¶
delay¶
- (delay expression)
Wraps expression in a promise object without evaluating it. The expression will be evaluated later, on demand, when the promise is passed to
force. This is the fundamental building block of lazy evaluation in Cozenage.The promise memoises its result: the first call to
forceevaluates expression and caches the value inside the promise object. Every subsequent call toforceon the same promise returns the cached value immediately, without re-evaluating expression. As a consequence, any side effects inside expression occur at most once.delayis a special form, not a procedure. If it were a procedure, its argument would be evaluated eagerly beforedelaycould defer it, defeating the purpose entirely.- Param expression:
The expression to defer. Not evaluated at call time.
- Type expression:
any
- Return:
A promise object encapsulating expression and the enclosing environment.
- Rtype:
promise
Example:
--> (define p (delay (+ 1 2))) --> p #<promise object:unevaluated> --> (force p) 3 --> (force p) 3
Side effects fire exactly once, regardless of how many times the promise is forced:
--> (define count 0) --> (define p (delay (begin (set! count (+ count 1)) count))) --> (force p) 1 --> (force p) 1 --> count 1
delayis commonly used to defer expensive computations until their result is actually needed:--> (define result (delay (some-expensive-computation))) --> (if (need-result?) ... (force result) ... "skipped")
Note
The effect of expression returning multiple values is unspecified.
delayis intended for use with single-valued expressions.See also
delay-force¶
- (delay-force expression)
Similar to
delay, but intended for use in iterative lazy algorithms — those that construct long chains of nested promises. Wheredelaywould cause each link in the chain to be forced recursively (consuming stack space proportional to the chain length),delay-forcecausesforceto iterate rather than recurse, using a trampoline mechanism. This allows iterative lazy algorithms to run in constant stack space.(delay-force expression)is conceptually similar to(delay (force expression)), with the important difference that forcing the result will in effect result in a tail call to(force expression), whereas forcing(delay (force expression))might not.The expression passed to
delay-forcemust evaluate to a promise when forced. If it returns any other type,forcewill signal an error.delay-forceis a special form for the same reason asdelay: its argument must not be evaluated at call time.- Param expression:
An expression that must evaluate to a promise. Not evaluated at call time.
- Type expression:
any
- Return:
A promise object in the
LAZYstate, which will trampoline through further promises when forced.- Rtype:
promise
Example:
The classic illustration is a stream traversal that would otherwise build a chain of deferred
forcecalls proportional to the depth of traversal. Usingdelay-force, this runs in constant stack space regardless ofn:--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (define (stream-ref-df s n) ... (if (= n 0) ... (make-promise (head s)) ... (delay-force (stream-ref-df (tail s) (- n 1))))) --> (force (stream-ref-df nats 10000)) 10000
Without
delay-force, a naïve recursive implementation would consume stack depth proportional ton, eventually overflowing for large values. Withdelay-force,forcetrampolines through the chain iteratively.Note
delay-forceis an advanced form. For straightforward lazy evaluation,delayis sufficient. Reach fordelay-forceonly when you are building algorithms that produce deeply nested promise chains — typically when writing recursive stream operations.
stream¶
- (stream head tail)
Constructs a stream node — the fundamental unit of a stream. head is evaluated eagerly and becomes the first element of the stream. tail is wrapped in a promise without being evaluated, and will only be forced when the next element of the stream is requested.
This eager/lazy split is what makes infinite streams possible: the head is a concrete value, but the tail is a deferred computation that may itself return another stream node, another deferred computation, and so on, without limit.
streamis a special form because its second argument must not be evaluated at construction time. A recursive stream definition such as the one below would loop forever ifstreamwere a procedure:(define (integers-from n) (stream n (integers-from (+ n 1)))) ; tail is NOT evaluated here.
- Param head:
The first element of the stream. Evaluated immediately.
- Type head:
any
- Param tail:
An expression producing the remainder of the stream. Wrapped in a promise; not evaluated until needed.
- Type tail:
any
- Return:
A stream node whose head is the value of head and whose tail is a promise encapsulating tail.
- Rtype:
stream
Example:
--> (define (integers-from n) ... (stream n (integers-from (+ n 1)))) --> (define nats (integers-from 0)) --> nats #[0 ... #<promise object:unevaluated>] --> (head nats) 0 --> (head (tail nats)) 1 --> (head (tail (tail nats))) 2
Streams are more naturally constructed using
iteratefor simple sequences, butstreamis the primitive from which all streams are ultimately built:--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (take 5 nats) (0 1 2 3 4)
A finite stream can be terminated by returning
'()as the tail expression:--> (define (list->stream lst) ... (if (null? lst) ... '() ... (stream (car lst) (list->stream (cdr lst))))) --> (define s (list->stream '(1 2 3))) --> (take 3 s) (1 2 3)
See also
Procedures¶
force¶
- (force promise)
Forces the value of promise. If promise has not yet been evaluated, its expression is evaluated now, the result is cached inside the promise, and that result is returned. If promise has already been forced, the cached value is returned immediately without re-evaluation.
If promise is not a promise, it is returned unchanged. This allows
forceto be called defensively on values that may or may not be promises.Promises created with
delay-forceare handled transparently via a trampoline:forceiterates through chains of lazy promises rather than recursing, so deeply iterative lazy algorithms run in constant stack space.Re-entrant forcing — forcing a promise whose evaluation has already begun — is detected and signalled as an error.
- Parameters:
promise (promise) – A promise, or any other value.
- Returns:
The value of the promise, or promise itself if it is not a promise.
- Return type:
any
Example:
--> (force (delay (+ 1 2))) 3
Forcing a non-promise returns the value unchanged:
--> (force 42) 42 --> (force "hello") "hello"
Memoisation — the body is evaluated exactly once:
--> (define count 0) --> (define p (delay (begin (set! count (+ count 1)) count))) --> (force p) 1 --> (force p) 1 --> count 1
Forcing a
delay-forcepromise trampolines through the chain iteratively, regardless of depth:--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (define (stream-ref-df s n) ... (if (= n 0) ... (make-promise (head s)) ... (delay-force (stream-ref-df (tail s) (- n 1))))) --> (force (stream-ref-df nats 10000)) 10000
See also
make-promise¶
- (make-promise obj)
Returns a promise that, when forced, will return obj. Unlike
delay, which defers evaluation of its argument,make-promiseis a procedure and evaluates obj immediately — it wraps an already-computed value in a promise rather than deferring a computation.If obj is already a promise, it is returned unchanged. This makes
make-promiseidempotent: wrapping a promise in another promise has no effect.make-promiseis useful when an interface requires a promise but you already have a concrete value — for example, as a base case in a recursivedelay-forcechain.- Parameters:
obj (any) – The value to wrap, or a promise to return unchanged.
- Returns:
A forced promise whose value is obj, or obj itself if it is already a promise.
- Return type:
promise
Example:
--> (define p (make-promise 42)) --> (promise? p) #true --> (force p) 42
Passing an existing promise returns it unchanged:
--> (define p (delay (+ 1 2))) --> (eq? p (make-promise p)) #true
Typical use as a base case in a
delay-forcechain:--> (define (stream-ref-df s n) ... (if (= n 0) ... (make-promise (head s)) ... (delay-force (stream-ref-df (tail s) (- n 1))))) --> (force (stream-ref-df nats 100)) 100
promise?¶
- (promise? obj)
Returns
#trueif obj is a promise,#falseotherwise. A promise is any object created bydelay,delay-force, ormake-promise.promise?returns#trueregardless of whether the promise has been forced or not — both unevaluated and memoised promises satisfy the predicate.- Parameters:
obj (any) – The object to test.
- Returns:
#trueif obj is a promise,#falseotherwise.- Return type:
boolean
Example:
--> (promise? (delay 42)) #true --> (promise? (make-promise 42)) #true --> (promise? 42) #false --> (promise? "hello") #false
A forced promise is still a promise:
--> (define p (delay (+ 1 2))) --> (force p) 3 --> (promise? p) #true
stream?¶
- (stream? obj)
Returns
#trueif obj is a stream,#falseotherwise. A stream is any object constructed by thestreamspecial form, or returned by a stream-producing procedure such asiterate,collect,select, orweave.Note that the empty stream — represented by
'()— is not considered a stream by this predicate. Usestream-null?to test for the empty stream.- Parameters:
obj (any) – The object to test.
- Returns:
#trueif obj is a stream node,#falseotherwise.- Return type:
boolean
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (stream? nats) #true --> (stream? 42) #false --> (stream? '()) #false --> (stream? (delay 42)) #false
See also
stream-null?¶
- (stream-null? obj)
Returns
#trueif obj is the empty stream,#falseotherwise. The empty stream is represented by'(). This is the natural terminator for finite streams, and is what stream-producing procedures such aslist->streamyield when their input is exhausted.Use
stream-null?rather thannull?when writing code that operates on streams, to make the intent clear to the reader.- Parameters:
obj (any) – The object to test.
- Returns:
#trueif obj is the empty stream,#falseotherwise.- Return type:
boolean
Example:
--> (stream-null? '()) #true --> (stream-null? (list->stream '())) #true --> (stream-null? (list->stream '(1 2 3))) #false --> (stream-null? 42) #false
Typical use when recursing over a finite stream:
--> (define (stream-length s) ... (if (stream-null? s) ... 0 ... (+ 1 (stream-length (tail s))))) --> (stream-length (list->stream '(1 2 3 4 5))) 5
See also
Stream Constructors¶
iterate¶
- (iterate proc seed)
Returns an infinite stream whose first element is seed, and each subsequent element is the result of applying proc to the previous one. The stream produced is
(seed (proc seed) (proc (proc seed)) ...).iterateis the general-purpose stream constructor. Most infinite streams can be expressed naturally as an iteration over a seed value, making explicit recursivestreamdefinitions unnecessary in the common case.- Parameters:
proc (procedure) – A procedure of one argument, used to compute each successive element from the previous one.
seed (any) – The first element of the stream, and the initial input to proc.
- Returns:
An infinite stream starting at seed.
- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (take 5 nats) (0 1 2 3 4)
Powers of two:
--> (define powers-of-2 (iterate (lambda (n) (* n 2)) 1)) --> (take 8 powers-of-2) (1 2 4 8 16 32 64 128)
iteratecomposes naturally withcollectandselect:--> (define odds (select odd? (iterate (lambda (n) (+ n 1)) 0))) --> (take 5 odds) (1 3 5 7 9)
See also
list->stream¶
- (list->stream list)
Converts a proper list into a finite stream. The resulting stream contains the same elements in the same order as list, terminated by the empty stream
'().This is the primary bridge between the list and stream worlds, allowing finite data held in lists to be processed by stream operations. The conversion is lazy: only as much of list as is actually consumed by subsequent stream operations will be traversed.
If list is empty, the empty stream
'()is returned immediately.- Parameters:
list (list) – A proper list to convert.
- Returns:
A finite stream containing the elements of list, or
'()if list is empty.- Return type:
stream
Example:
--> (define s (list->stream '(1 2 3 4 5))) --> (head s) 1 --> (take 3 s) (1 2 3) --> (stream? s) #true
Stream operations can be applied directly to the result:
--> (take 5 (collect (lambda (n) (* n n)) ... (list->stream '(1 2 3 4 5)))) (1 4 9 16 25) --> (take 3 (select odd? (list->stream '(1 2 3 4 5)))) (1 3 5)
See also
Stream Accessors¶
head¶
- (head stream)
Returns the first element of stream. The head is always a fully evaluated value — it is computed eagerly when the stream node is constructed.
headis the stream analogue ofcar.- Parameters:
stream (stream) – A stream node.
- Returns:
The first element of stream.
- Return type:
any
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (head nats) 0 --> (head (tail nats)) 1 --> (head (tail (tail nats))) 2
tail¶
- (tail stream)
Returns the remainder of stream after its first element. The tail is stored as a promise and is forced automatically by
tail— the caller receives the next stream node (or'()if the stream is exhausted) without needing to callforceexplicitly.If stream is the empty stream
'(), the empty stream is returned.tailis the stream analogue ofcdr, with the addition of automatic forcing.- Parameters:
stream (stream) – A stream node, or the empty stream.
- Returns:
The next stream node, or
'()if the stream is exhausted.- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (tail nats) #[1 ... #<promise object:native>] --> (head (tail nats)) 1
Chaining
tailcalls traverses the stream element by element:--> (head (tail (tail (tail nats)))) 3
Calling
tailon the empty stream returns the empty stream:--> (tail '()) ()
at¶
- (at n stream)
Returns the element at zero-based index n in stream, forcing as many tail promises as necessary to reach that position. Elements before index n are traversed but not returned.
If the stream is exhausted before index n is reached, an error is signalled.
- Parameters:
n (integer) – A non-negative integer index.
stream (stream) – A stream to index into.
- Returns:
The element at position n.
- Return type:
any
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (at 0 nats) 0 --> (at 5 nats) 5 --> (at 10000 nats) 10000
Works on any stream, including transformed ones:
--> (define squares (collect (lambda (n) (* n n)) nats)) --> (at 5 squares) 25
Stream Sequence Operations¶
take¶
- (take n stream)
Returns a list containing the first n elements of stream, forcing exactly n tail promises. If stream contains fewer than n elements, all available elements are returned.
The result is a plain list, not a stream.
takeis the primary way to extract a finite, fully evaluated sequence from a potentially infinite stream.- Parameters:
n (integer) – The number of elements to take.
stream (stream) – A stream to take elements from.
- Returns:
A list of the first n elements of stream.
- Return type:
list
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (take 5 nats) (0 1 2 3 4) --> (take 1 nats) (0) --> (take 0 nats) ()
takeis commonly used as the final step in a stream pipeline to materialise results:--> (take 5 (select even? nats)) (0 2 4 6 8) --> (take 5 (collect (lambda (n) (* n n)) nats)) (0 1 4 9 16)
Note
takeis an eager consumer — it forces n elements immediately. For stream operations that remain lazy, seecollect,select, anddrop.
drop¶
- (drop n stream)
Returns the stream that remains after discarding the first n elements of stream. Unlike
take, the result is a stream, not a list — further lazy operations can be applied to it.If stream contains fewer than n elements, the empty stream is returned.
- Parameters:
n (integer) – The number of elements to discard.
stream (stream) – A stream to drop elements from.
- Returns:
The stream beginning at element n.
- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (head (drop 5 nats)) 5 --> (take 3 (drop 10 nats)) (10 11 12)
dropandtakecan be combined to extract an arbitrary slice of a stream:--> (take 5 (drop 100 nats)) (100 101 102 103 104)
Stream Transformers¶
collect¶
- (collect proc stream)
Returns a new stream formed by applying proc to each element of stream. The head is transformed eagerly; each subsequent element is transformed lazily as the stream is consumed.
collectis the stream analogue ofmap. It is safe to apply to infinite streams — no elements beyond the head are forced until the resulting stream is consumed.- Parameters:
proc (procedure) – A procedure of one argument to apply to each element.
stream (stream) – The input stream.
- Returns:
A new stream of transformed elements.
- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (take 5 (collect (lambda (n) (* n 2)) nats)) (0 2 4 6 8) --> (take 5 (collect (lambda (n) (* n n)) nats)) (0 1 4 9 16)
collectcalls can be chained:--> (define doubled (collect (lambda (n) (* n 2)) nats)) --> (take 5 (collect (lambda (n) (+ n 1)) doubled)) (1 3 5 7 9)
collectandselectcompose naturally:--> (take 5 (collect (lambda (n) (* n n)) ... (select even? nats))) (0 4 16 36 64)
select¶
- (select pred stream)
Returns a new stream containing only those elements of stream for which pred returns a true value. Elements are tested lazily — only as many elements as are needed to satisfy the next request are ever forced.
selectis the stream analogue offilter. It is safe to apply to infinite streams, provided the resulting stream is consumed with a bounding operation such astakeorat.- Parameters:
pred (procedure) – A predicate procedure of one argument.
stream (stream) – The input stream.
- Returns:
A new stream containing only elements that satisfy pred.
- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (take 5 (select even? nats)) (0 2 4 6 8) --> (take 5 (select odd? nats)) (1 3 5 7 9) --> (at 3 (select even? nats)) 6
selectandcollectcompose freely:--> (take 5 (select even? ... (collect (lambda (n) (* n n)) nats))) (0 4 16 36 64)
weave¶
- (weave stream1 stream2)
Returns a new stream of pairs, combining stream1 and stream2 element-wise. The head of each pair is taken from stream1; the tail from stream2. The resulting stream is as long as the shorter of the two inputs — if either stream is exhausted, the result terminates.
weaveis the stream analogue ofzip. It is safe to apply to infinite streams.- Parameters:
stream1 (stream) – The first input stream.
stream2 (stream) – The second input stream.
- Returns:
A new stream of pairs
(a . b)where a is from stream1 and b is from stream2.- Return type:
stream
Example:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (define powers (iterate (lambda (n) (* n 2)) 1)) --> (take 4 (weave nats powers)) ((0 . 1) (1 . 2) (2 . 4) (3 . 8))
When one stream is finite, the result terminates at the shorter stream:
--> (take 5 (weave (list->stream '(a b c)) nats)) ((a . 0) (b . 1) (c . 2))
Pairs produced by
weavecan be processed withcollect:--> (take 5 (collect (lambda (p) (+ (car p) (cdr p))) ... (weave nats powers))) (1 3 6 11 20)
Stream Reduction¶
reduce¶
- (reduce proc init stream)
Folds proc over stream, accumulating a result. Starting with init as the initial accumulator,
reduceapplies(proc acc element)for each element in turn, where acc is the current accumulator and element is the current stream element. The final accumulator value is returned.reduceaccepts both streams and plain lists as its third argument, making it polymorphic over both sequence types.Warning
reduceis an eager consumer — it must traverse the entire input to produce a result. Applying it to an infinite stream will cause the interpreter to loop forever. Always bound infinite streams withtakebefore reducing:(reduce + 0 (take 10 nats)) ; Safe. (reduce + 0 nats) ; Diverges.
- Parameters:
proc (procedure) – A procedure of two arguments: the current accumulator and the current element.
init (any) – The initial accumulator value.
stream (stream or list) – A finite stream or proper list to fold over.
- Returns:
The final accumulated value.
- Return type:
any
Example:
--> (reduce + 0 (list->stream '(1 2 3 4 5))) 15 --> (reduce * 1 (list->stream '(1 2 3 4 5))) 120
Reducing over a bounded slice of an infinite stream:
--> (define nats (iterate (lambda (n) (+ n 1)) 0)) --> (reduce + 0 (take 10 nats)) 45
reducealso accepts plain lists directly:--> (reduce max 0 (list->stream '(3 1 4 1 5 9 2 6))) 9 --> (reduce string-append "" (list->stream '("a" "b" "c" "d"))) "abcd"