Email list hosting service & mailing list manager

stream-define Richard Kelsey (12 Feb 2003 02:06 UTC)
Re: stream-define bear (12 Feb 2003 05:34 UTC)
Re: stream-define bear (12 Feb 2003 16:07 UTC)

stream-define Richard Kelsey 12 Feb 2003 02:05 UTC

I think you should replace STREAM-DEFINE with a non-DEFINE version.
In introducing STREAM-DEFINE the SRFI says:

  ... with the (delay (force ...)) hidden within stream-define, which
  is the syntax used to create a function that returns a stream:

The problem is that STREAM-DEFINE is syntax used to define a function
that returns a stream, not just to create such a function.  What about
the LAMBDA's that are not the value in a DEFINE?  Not to mention those
benighted individuals who don't like the (DEFINE (F ...) ...) syntax
in the first place.

You could replace STREAM-DEFINE with STREAM-DELAY:

 (define-syntax stream-delay
   (syntax-rules ()
     ((stream-delay x)
      (make-stream (delay (force (stream-promise x)))))))

Then (stream-define (foo a b) stuff) could be written as

 (define (foo a b) (stream-delay stuff))

which has the advantage of making it possible to control which expressions
get delayed and which don't.  But mostly, it makes it clearer what is going
on.  Unless I am missing something the code in the SRFI uses STREAM-DEFINE
more often than necessary.  Take MAP2 and COUNTDOWN2 from the initial
explanation of even and odd streams:

  (define (map2 func strm)
    (delay (force (if (nil2? strm)
                      nil2
                      (cons2 (func (car1 strm))
                             (map2 func (cdr2 strm)))))))

  (define (countdown2 n)
    (delay (force (cons2 n (countdown2 (- n 1))))))

The DELAY-FORCE in MAP2 is needed to delay the call to NIL2?.  Without
it MAP2 wouldn't return a fully-lazy stream.  But the DELAY-FORCE in
COUNTDOWN2 is only delaying a CONS2 expression, which expands into a
DELAY.  There is no utility in delaying the creation of a delay.

The reference implementation itself uses STREAM-DEFINE in many places
where DEFINE would work, including the definitions of STREAM-FROM,
STREAM-FROM-TO, STREAM-REPEAT, and STREAM-ITERATE.  Many of the
functions that do need a delay would benefit from moving the delay
after some initial error checks.  For example, writing STREAM-UNIQ
using STREAM-DELAY allows it to signal errors when it is called
instead of waiting until the result is forced:

  (define (stream-uniq eql? strm)
    (if (not (procedure? eql?))
        (stream-error "non-functional argument to uniq"))
    (if (not (stream? strm))
        (stream-error "non-stream argument to uniq"))
    (stream-delay (if (stream-null? strm)
                      stream-null
                      (let ((first (stream-car strm)))
                        (stream-cons first
                        (stream-uniq-aux eql? strm first)))))

           -Richard Kelsey