Structure and Interpretation of Computer Programs (SICP) 3.5.2 introduces infinite streams:
(define ones
(cons-stream 1 ones))
This code doesn't work in DrRacket, with the error:
ones: undefined; cannot reference an identifier before its definition
Other code like this:
(define (integers-starting-from n)
(cons-stream n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
produce error:
Interactions disabled
(fall in infinite loop?)
So far as I read(SICP), the key point of implementing an infinite stream is delayed evaluation:
(define (delay exp)
(lambda () exp))
(define (cons-stream a b)
(cons a
(delay b)))
With this used in cons-stream
, infinite stream still illegal.
Such data structure reminds me of recursive function in whose definition self-call is legal (in compiling) within or without an actual exit.
Why is it illegal for a value to reference itself? Even the reference has been delayed?
Could infinite stream supported by other programming language?
If not, is it about the way processor deal with assembly language? The data stack stuff?
When you make a procedure the arguments are already evaluated when you start the body of the procedure. Thus delay
would not do anything since it's already computed at that time. cons-stream
need to be a macro.
DrRacket is not one language implementation. It's an IDE that supports lots of languages. One of them is a SICP compatibility language. I mananged to run your code without errors using this code in DrRacket:
#!planet neil/sicp
(define ones
(cons-stream 1 ones))
(define (integers-starting-from n)
(cons-stream n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
It works like a charm. The default language in DrRacket, #!racket
, has streams too, but the names are different:
#!racket
(define ones
(stream-cons 1 ones))
(define (integers-starting-from n)
(stream-cons n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
However for Scheme you should use SRFI-41 that you can use from both #!racket
(using (require srfi/41)
) and #!r6rs
(and R7RS-large when it's finished)
(import (rnrs)
(srfi :41))
(define ones
(stream-cons 1 ones))
(define (integers-starting-from n)
(stream-cons n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
To roll your own SICP streams in both #!racket
and #!r6rs
you can use define-syntax
(define-syntax stream-cons
(syntax-rules ()
((_ a d) (cons a (delay d)))))
Note that RSFI-41 and rackets own stream library for #!racket
delays both values in a stream-cons
and not just the tail as in SICP version here.
It's because the expression in a define
is evaluated before the name is bound to a value. It tries to evaluate (cons-stream 1 ones)
before ones
is defined, causing an error.
The reason this works fine for functions is that the body of a function is not evaluated when the function is. That is, to evaluate (lambda (x) (f x))
, the language returns a function without looking at its body. Since
(define (f x) (f x))
is syntax sugar for defining a lambda, the same logic applies.
Did you define cons-stream
yourself as above? If you make cons-stream
a normal function it won't work properly. Since Scheme is strict by default, arguments are evaluated before the function gets called. If cons-stream
is a normal function, b
would get evaluated completely before being passed to delay
, making you hit an infinite loop.
cons-stream
in SICP is a "special form" rather than a function, which means that it can control how its arguments are evaluated.
If you use the stream-cons
and other stream-
operations built into Racket, you'd get the behavior you want.
Finally, some other languages do allow values to reference themselves. A great example is Haskell, where this works because everything is lazy by default. Here's a Haskell snippet defining ones
to be an infinite list of ones. (Since everything is lazy, Haskell lists behave just like Scheme streams):
ones = 1 : ones
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With