Am Fr., 30. Sept. 2022 um 21:15 Uhr schrieb John Cowan <[email protected]>:

>
>
> On Fri, Sep 30, 2022 at 3:32 AM Marc Nieper-Wißkirchen <
> [email protected]> wrote:
>
> In the last draft (#2), all changes based on all comments and
>> extensive reviews on the mailing list so far were incorporated.  Since
>> then, there was no more discussion.  Thus, I would propose to declare
>> finalization in a week.  On the other hand, should more than editorial
>> changes become necessary during this period, we would see the third
>> draft instead.
>>
>
> 1. I strongly recommend that a faithful implementation of CL's
> `unwind-protect` be added to this SRFI.  Scheme programmers have a tendency
> to treat `dynamic-wind` as if it served the same purpose, which it does
> not; making it available under its own name will encourage its proper use.
> The definition is available at <http://clhs.lisp.se/Body/s_unwind.htm>.
>

I have never taken a look at CL's `unwind-protect`.  You seem to imply that
it is different from a `dynamic-wind` without a BEFORE thunk.  Can you
explain the purpose/mechanics of `unwind-protect`?  I can then add it.


> 2. It seems to me that having an iterator for the empty list that returns
> an iterator for the empty list as its second value is easily misused.  A
> failure to check for the first value being #f causes an infinite loop.
> Instead, the second argument should be a thunk that raises an error.
>

I agree with you that the iterator protocol in SRFI 226 is not optimal.  I
copied it from Racket because `continuation-mark-set->iterator` comes from
there.

I think the incompatibility that would be created by your suggestion is
actually not a bad one.  As you say, using the returned thunk after the
iterator has run out of values is probably a programming mistake.

Instead of returning a thunk that raises an error, we could also specify
that #f is delivered as a "thunk" instead.  Calling #f would also raise an
error, but it would also enable to use this particular iterator protocol
for lists that contain the value #f (not relevant here, but possibly
somewhere else).  What do you think?


> 3. I recommend switching timeouts from seconds to jiffies, so as to
> maximize the use of cheap fixnum arithmetic.  Exact integers are real, of
> course, but a second is a long time.
>

Technically, I agree entirely.  This would, however, produce an
incompatibility with SRFI 18 that may be hard to detect (because the types
are not disjoint).

So I am a bit undecided here.


> 4.  I don't think that &thread-timeout is necessarily an &error or even
> &serious; for example, you may be running a background thread to do work
> for a while, but want to cut it off if it runs too long.  It should be a
> direct subtype of &condition.
>

I have to think about it.  Wouldn't your reasoning apply to
`&uncaught-exception` and `&thread-already-terminated` as well?  From the
definition of the conditions in R6RS, `&serious` seems to be a better fit
than `&error`.

Looking at it from a different perspective: What could go wrong if we left
it as is?


> 5. I recommend the addition of a simple thread-runner facility.  A
> thread-runner is an opaque object with which threads can be registered by
> passing it as an optional argument to `make-thread!`.  The procedure
> `with-thread-runner` takes a procedure *proc* and invokes it with a newly
> allocated thread-runner.  The purpose of `with-thread-runner` is to keep
> the forking and joining of threads within the lexical scope of *proc*, so
> as to avoid thread-level spaghetti code.
>
> When *proc* returns, `with-thread-runner` waits for all registered
> threads to terminate normally or abnormally, and then returns a list of
> the threads in arbitrary order. It is then possible to use `thread-wait`
> on any of these threads to extract their results.  `With-thread-runner`
> should perhaps take a second, optional, argument which is a timeout.
>
> Acknowledgement: the Python library Trio.
>

I can add such a facility.  The API you suggest would need a modification
of `make-thread`.  This would make the thread-runner facility not
implementable on top of the primitives we currently have.  Thus, I would
suggest the following slight modification:

A thread runner is a procedure that takes one argument, a thread.  When a
thread runner is called with such an argument, the thread is registered
with the thread runner.  (This works well because the creation and the
start of threads are separated.)  When the thread runner is called with no
argument, we could make it unregister and return an arbitrary thread it
holds.


> ===========
>
> The following unnumbered points are editorial.
>
> Spelling errors (I don't know if Arthur does a spellcheck or not):
> compatiblity -> compatibility
>


> <formals>s -> <formals>
> <body>s -> <bodies>
>

I think I saw this style once, but let's correct it as John suggests.



> interthread -> inter-thread
> multithreaded -> multi-threaded
> Need whitespace after `make-parameter` in the first sentence of its
> definition
> parametrization -> parameterization
> reparameterized -> re-parameterized
> sublibraries -> sub-libraries
>
> In addition, I suggest "singularized -> are in the singular" and "is a
> special form" -> "is syntax".  "Instate" is a known English verb, but it is
> rare: "install" is clearer.
>

I will adopt these suggestions. Thanks!


> "the call-with-non-composable-continuation defined here is a conservative
> extension of the call-with-non-composable-continuation of earlier Scheme
> reports": I think the second instance should be
> "call-with-current-continuation".
>

Yep.



>
> Is there any reason to use "parameterization" (as a concrete noun) rather
> than "dynamic environment"?  If the two are being used in distinct senses,
> I don't see it.  (This change would affect the names of certain procedures.)
>

The dynamic environment holds the current exception handler (according to
the Scheme reports); it is not part of the parameterization, though.


> The claim that the SRFI's definition of make-promise breaks R7RS
> compatibility should be removed, because adding new arguments to a
> procedure is backward compatible in all RnRS except R6RS.
>

The problem with R7RS's `make-promise` is that it is idempotent; for
example, a promise argument value will be simply returned.  On the other
hand, SRFI 226's `make-promise` respects the nesting of promises.

So we cannot safely use R7RS's `make-promise` to wrap general values into a
promise that can be forced later as `(force (make-promise VALUE))` is, in
general not equivalent to VALUE.

Given what we have discussed about promises in the large language (disjoint
promise objects, etc.), the R7RS behavior of `make-promise` is even less
helpful.

I don't think this incompatibility is a big problem as we already have the
incompatibility in the dynamic environment, in which a promise is forced.
I propose something like `(scheme promise)` and deprecating the old
`(scheme lazy)`.



>
> Add a note to 5.12.4 that "condition variable" is a misleading term, as it
> is not a variable but an object.
>

Good point.


>
> It's rather hard to figure out what library or libraries a given
> identifier is contained in.  Having found the relevant identifier, one has
> to search back arbitrarily far in the document to find the relevant
> library.  I recommend a single mapping table somewhere in the SRFI, either
> in the style of the R7RS appendix or an inversion of it.
>

Either that or I expand the entry format so that a library is also shown.
(This may even be a good idea for future reports.)

Thank you very much for another round of careful reading!

Marc

Reply via email to