Stream-filter and space leaks Phil Bewig (20 Feb 2003 14:08 UTC)
Re: Stream-filter and space leaks Richard Kelsey (20 Feb 2003 18:41 UTC)
Re: Stream-filter and space leaks Phil Bewig (21 Feb 2003 09:57 UTC)
Re: Stream-filter and space leaks Richard Kelsey (21 Feb 2003 23:18 UTC)
Re: Stream-filter and space leaks sperber@xxxxxx (21 Feb 2003 10:04 UTC)
Re: Stream-filter and space leaks Richard Kelsey (21 Feb 2003 15:06 UTC)
Re: Stream-filter and space leaks Matthias Neubauer (25 Feb 2003 16:01 UTC)
Re: Stream-filter and space leaks Richard Kelsey (25 Feb 2003 18:25 UTC)
Re: Stream-filter and space leaks Matthias Neubauer (25 Feb 2003 21:32 UTC)
Re: Stream-filter and space leaks Richard Kelsey (25 Feb 2003 22:11 UTC)
Re: Stream-filter and space leaks sperber@xxxxxx (26 Feb 2003 08:18 UTC)
Re: Stream-filter and space leaks Matthias Neubauer (26 Feb 2003 13:31 UTC)
Re: Stream-filter and space leaks sperber@xxxxxx (11 Mar 2003 14:58 UTC)
Re: Stream-filter and space leaks Matthias Neubauer (19 Mar 2003 15:27 UTC)
Re: Stream-filter and space leaks felix (22 Mar 2003 09:49 UTC)
Re: Stream-filter and space leaks felix (22 Mar 2003 09:56 UTC)

Re: Stream-filter and space leaks Richard Kelsey 21 Feb 2003 15:05 UTC

   From: xxxxxx@informatik.uni-tuebingen.de (Michael Sperber [Mr.  Preprocessor])
   Date: Fri, 21 Feb 2003 11:04:03 +0100

   The code you posted seems to be essentially the definition of
   DELAY/FORCE from Scheme 48.  However, Phil's old code does leak in
   Scheme 48 and doesn't leak with the proposed fix.

Right.  I was mistaken about the source of the leak.  This time around
I have actually tested the code.

The problem is that the Scheme 48 version of DELAY, as well as the
R5RS version, keeps the delayed thunk around in case it is reentrant:

 > (define f (let ((first? #t))
               (delay (if first?
                          (begin
                            (set! first? #f)
                            (force f))
                          'second))))
 ; no values returned
 > (force f)
 'second

In the case of the SRFI version of STREAM-FILTER the thunk is closed over
the filtered stream.  The new version drops that thunk's reference to the
stream before forcing it.

DELAY and FORCE can be modified to plug the leak, at the cost of making
promises non-reentrant.  In the version below the promise's thunk clears
the promise's reference to itself before executing the closed-over
expression.  The SRFI's version of STREAM-FILTER doesn't leak when using
MY-DELAY and MY-FORCE.

 (define-syntax my-delay
   (syntax-rules ()
     ((delay ?exp) (make-promise (lambda (clear-me)
                                   (set-car! clear-me #f)
                                   ?exp)))))

 (define (make-promise thunk)
   (let ((already-run? #f)
         (thunk-then-result (list thunk)))
     (lambda ()
       (if already-run?
           (car thunk-then-result)
           (cond ((procedure? (car thunk-then-result))
                  (let ((result ((car thunk-then-result) thunk-then-result)))
                    (cond ((not already-run?)
                           (set! already-run? #t)
                           (set-car! thunk-then-result result)))
                    (car thunk-then-result)))
                 (else
                  (error "circular promise")))))))

 (define (my-force promise)
   (promise))

As promised, promises are no longer reentrant:

 > (define g (let ((first? #t))
               (my-delay (if first?
                             (begin
                               (set! first? #f)
                               (my-force g))
                             'second))))
 ; no values returned
 > (my-force g)

 Error: circular promise
 1>

This might be acceptable for streams.

   This is just a note---I intend to check next week why this is so.

I was unwilling to wait...
                                            -Richard