Am Do., 5. Aug. 2021 um 01:58 Uhr schrieb John Cowan <xxxxxx@ccil.org>:


On Wed, Aug 4, 2021 at 5:50 PM Marc Feeley <xxxxxx@iro.umontreal.ca> wrote:
 
Both of you are correct.  The semantically right thing is for the delay body to inherit the dynamic environment (similarly to what SRFI 18 threads do). 

I am not convinced of that.  It would make promises much heavier to have them close over the global dynamic environment and the dynamic-wind stack, as threads do.  If you do want the heavier behavior, you can use threads.

Threads, especially when they are implemented as native threads are much more heavy-weight than that, so that cannot be a substitute.

But note that threads do not close over the dynamic-wind stack but start with an empty one.  It would be best if promises, threads, and futures (from your proposal) all behaved the same in this regard.  What is inherited from the dynamic environment by SRFI 18 threads (and what would be the semantically correct thing for promises in Marc's and my opinion) is basically the current parameterization (the set of all parameter cells).

Saving this doesn't have to be heavier-weight than just writing one value in an underlying thread-local cell if the parameterization is implemented as a fast table (Racket has an especially clever implementation of it in its CS version).  So semantically correct promises can be as light-weight as they currently already are.
 
My upcoming SRFI on control operators will say a word on this.

 
Currently the delay form is broken for things like

    (define p (delay (display "foo")))

because the port on which foo is output is not clear… it depends on where the first call of (force p) is done, and this could depend on various factors not locally apparent.

That's true.  But there are other problems: for example, closing over the current-output-port parameter does not help if the underlying port itself has been closed in the main program.

These two problems are (fundamentally) different:

Closing a port is a destructive, non-pure operation.  It has always been clear that lazy evaluation does not work well with non-pure code (forcing Haskell to be a pure language). 

Dynamically binding a parameter, on the other hand, should be considered a pure operation, which, in effect, "just" saves typing in passing arguments around explicitly.  With the broken promise semantics of R7RS, however, binding a parameter becomes an impure operation, which is wrong. And parameters would no longer be equivalent to implicitly passed arguments.

PS SRFI 155 suggests resolving any incompatibility with previous standards by deprecating (scheme lazy) and offering the "corrected" version in (scheme promise).