Reentrancy-detecting, faster implementation Andre van Tonder 19 Oct 2006 21:50 UTC
Re: Reentrancy-detecting, faster implementation Eli Barzilay 20 Oct 2006 06:14 UTC

Reentrancy-detecting, faster implementation Andre van Tonder 19 Oct 2006 21:50 UTC

Here is another safe-for-space version of srfi-45 promises that detects
reentrant promises and should be faster.

It follows closely the description of the G-machine handling of tail calls
(enhanced with black holes) in the reference:

   Richard Jones - "Tail recursion without space leaks"

This implementation has the following advantages over the previous
implementations that were based on naive graph reduction:

  - It is faster (for a given data representation), since the root node is not
    overwritten on each iteration, but only after the final promise in a lazy
    chain is forced.

  - Reentrant promises are detected early and a runtime exception is raised for
    them.

This second property is not consistent with r5rs, but it is IMO a very useful
feature.  Here is a simple example where it raises an exception:

   (let ((p (delay (force p))))
     (force p))                  ==> Error: reentrant promise

Implementation:
===============

;; <promise> ::= (lazy   . <thunk of promise>)   (delayed     promise)
;;             | (value  . <object>)             (forced      promise)
;;             | (shared . <promise>)            (shared      promise)
;;             | (hole   . #f)                   (black-holed promise)

(define-syntax lazy
   (syntax-rules ()
     ((lazy exp) (cons 'lazy (lambda () exp)))))

(define-syntax delay
   (syntax-rules ()
     ((delay exp) (lazy (cons 'value exp)))))

(define (force root-node)

   (define (dispatch node)
     (let ((type    (car node))
           (content (cdr node)))
       (set-car! node 'shared)     ; maintain any sharing by
       (set-cdr! node root-node)   ; pointing back to root
       (case type
         ((lazy)   (dispatch (content)))
         ((value)  (set-car! root-node 'value)   ; overwrite root at end
                   (set-cdr! root-node content)
                   content)
         ((shared) (dispatch content))
         (else     (error "Invalid promise")))))

   (case (car root-node)
     ((lazy)   (let ((thunk (cdr root-node)))
                 (set-car! root-node 'hole)   ; blackhole root note so that
                 (set-cdr! root-node #f)      ; we do not hold on to chain
                 (dispatch (thunk))))
     ((value)  (cdr root-node))
     ((shared) (force (cdr root-node)))
     ((hole)   (error "Reentrant promise"))
     (else     (error "Invalid promise"))))

Andre