Jim Rees <xxxxxx@gmail.com> schrieb am Do., 5. Juli 2018 um 19:56 Uhr:
On Thu, Jul 5, 2018 at 10:56 AM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
Another idea occured to me: What if the implementation of (srfi 155) attaches the dynamic extent prior to the evaluation of "with-continuation-mark" ....

That would be (promise-extent promise), right?   

Yes, this is what I thought.

           (lambda (c)
               (promise-extent promise)     <----!!!
               (lambda ()
                ;; the dynamic extent here is the one we just
                ;; installed above.
                (with-continuation-mark key 
                     (vector c forcing-extent (promise-extent promise))
                   ((promise-value promise))))))))))

to the continuation mark added by this evaluation. Then the code called by with-continuation-mark can retrieve the dynamic extent and resurrect it, thus effectively deleting the just added continuation mark.

So, then delay gets re-written:

    (define-syntax delay
      (syntax-rules ()
        ((_ expr)
           (lambda (extent)
              (lambda ()
                (lambda (m)
                   (vector-ref m 2)
                   (lambda () expr)))))

Perhaps I misunderstand your suggestion -- but doesn't this just kill the whole tail-call-detection-mark feature?  If "expr" either immediately or eventually leads to a call to force in tail context, we can't detect it and unwind back to the encompassing force call.

Yes, this doesn't work.

Actually, I thought about reinstalling the previous extent in the existing invokation of `call-with-immediate-continuation-mark` in `force`. But this would only reduce memory usage if `force` is called in tail context. So I think we can ditch this idea.

Thus, we are left with the previous suggestion of `extent-remove-marks`, which may also be of interest in other use cases.

The only problem with `extent-remove-marks` is that it cannot be implemented portably. We have two possibilities: Either we are fine with that the portable implementation of SRFI 154 won't be a complete implementation of the spec (because `extent-remove-marks` woll have to be a no-op) or we explicitely allow `extent-remove-marks` to be a no-op. I incline toward the former.

Another minor problem is that any implementation of `parameterize` would have to be tied to the implementation of SRFI 155 if we don't want a space blow-up in the example with the dummy parameter. SRFI 155 would have to export at least one more primitive that can be used by an implementation of `parameterize` to decouple the implementations.

What primitive would be usable for your implementation?

That said, let's rethink the problem we are trying to solve here: I think the fundamental cause of the unwished-for space behaviour (in the original implementation) is that the code being delayed usually does not need the full dynamic extent that is being captured. In general, adding a continuation mark is not allowed to override previous marks of the same key. In the code being delayed, however, previous marks may not be accessible anymore and could be dropped. This happens basically when `with-continuation-mark` is in tail position of the expression being delayed.

So, one idea is to add a special mark to the frame in which the delayed expression is evaluated. Whenever `with-continuation-mark` is called with key `key` and sees that special mark in tail context, it can erase all previous marks associated with key `key`.


P.S.: Your dynamic-extent records store the marks in one field and the dynamic-wind handlers in another field. Have you thought about storing the dynamic-wind handlers as continuation marks? This would make reasoning easier because the dynamic extent would just be the set of continuation marks.

P.P.S.: As you have invested a lot of time playing with your implementation to improve the implementation of SRFI 155, I would like to ask you whether you want to become co-author of SRFI 155 to reflect your investment.