[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

stream-define

This page is part of the web mail archives of SRFI 40 from before July 7th, 2015. The new archives for SRFI 40 contain all messages, not just those from before July 7th, 2015.



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