Thank you for your comments!

Alex Shinn <xxxxxx@gmail.com> schrieb am Mi., 5. Juli 2017 um 17:21 Uhr:
Dynamic environment and dynamic extent are not the same thing.
The latter refers to the before and after thunks of dynamic-wind,
while the former refers only to parameter bindings.  I believe you
intend for promises to be run in the dynamic extent of the time
that they were created, not the environment.

Exactly. However, in SRFI 155 I am using the definition of "dynamic environment" as given in SRFI 154 and not that the narrower one of the R7RS (it is another question to be adressed for SRFI 154 whether it is a good idea to reuse the same name).

The reason why I am not using the term "dynamic extent" is that "dynamic extent" is a property of a call to a procedure. Instead of saying, "(force p)" is run in the dynamic environment of the original call to delay or delay-force, I would have to say that the dynamic extent of "(force p)" is enclosed by the dynamic extent of the original call to delay or delay-force. Such a wording would make the dynamic extent itself a dynamic thing: Depending on whether p is ever forced or not, the dynamic extent of delay or delay-force is extended by the call (force p) or not.

Anyway, all this is debatable and if someone comes up with a better wording that is also applicable to SRFI 154, I will be grateful.

And let me give one reason why I have extended the meaning of dynamic environment to include more applications of dynamic-wind than just parameterize: Any use of dynamic-wind which is working well in a purely functional programming setting will have the "before" thunk save the "outer" stage and setup up the "inner" stage for the "thunk" and will have the "after" thunk safe the current "inner" stage and setup the recent "outer" stage. While "before" and "thunk" may be non-pure (as in the reference implementation of parameter object) there combined net effect will be purely functional. And the effect of such a use of dynamic-wind is something like a generalized parameterize (e.g. instead of variable values being set and restore, we could have a file being open and closed, etc.).
 
Have you run any benchmarks for this?  It seems an extremely
heavyweight operation for something which should be very
lightweight.  The dependence on dynamic-wind also means
performance will be highly variable per implementation.

The sample implementation won't perform well on implementations, for which call/cc is costly due to the presence of dynamic-wind. While I don't think that a Scheme implementation for which one of Scheme's main selling points is too slow to be used effectively is, in my opinion, a very decent one, I could change the sample implementation not to rely on call/cc in most use cases of forcing.

I could define a parameter object that holds a value representing the current dynamic environment (or dynamic extent, see above). Then I would have to setup and restore this parameter object at every use of dynamic-wind and parameterize. When a promised is forced, one can then take a short route if the current dynamic environment is the same as the one of the promise, which is testable by using the parameter object.

For a Scheme implementor I would propose to use a method like this one; for the sample implementation it is less attractive because one would have to import dynamic-wind and parameterize from the sample implementation and not from (scheme base) to make the customized versions of dynamic-wind and parameterize work.
 
That said, I think one has to be very careful when arguing with the performance of current implementations. If one had done this in the past, Scheme probably would not have lexical bindings (apparently, there were days when it was assumed to be too costly when compared to dynamic bindings), or closures, or call/cc. Haskell would have not be a lazy programming language, and so on.

If we want to see Scheme showing the world the future of programming languages again, it won't help us to always take the lowest common denominator of all existing interesting implementations.

Anyway, the implementation method described above should provide a lightweight means for the "functional" promises of this SRFI and should not be hard to implement in an existing implementation, even if the implementation method is hard to achieve in the portable sample implementation (without having to override core Scheme procedures).

Note I think delay-force/lazy were a mistake.  Their semantics
are of course desired, but I think in retrospect it's sufficient to
require (delay (force x)) be statically detected and the same
guarantees made - this can be done portably.

Why do you think it was a mistake? The power of the language is exactly the same - whether you write (delay (force ...)) or (delay-force ...). The only difference seems to me that (scheme lazy) would pollute the namespace by one identifier less.

Or do you think that we would not have to tell users of Scheme to write "delay-force" instead of "(delay (force ...))" because the Scheme system would take care of that itself?
 
In any case, an implementation of (scheme lazy) is free to write:

(define-syntax delay
  (syntax-rules (force)
    ((delay (force x))
     (delay-force x))
    ((delay x)
     ...)))

If you want, I can make this mandatory for the delay as exported by SRFI 155. Yes, maybe that's a good idea.

--

Marc


-- 
Alex

To unsubscribe from this list please go to http://www.simplelists.com/confirm.php?u=3gmIi49Ouh6YpCluDZXWeokz7NgY9f9r