One more review. Vladimir Nikishkin (09 Oct 2021 09:56 UTC)
|
Re: One more review.
John Cowan
(10 Oct 2021 03:52 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(10 Oct 2021 13:40 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(07 Sep 2022 08:36 UTC)
|
Re: One more review.
Vladimir Nikishkin
(07 Sep 2022 14:22 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(07 Sep 2022 15:07 UTC)
|
Re: One more review.
John Cowan
(07 Sep 2022 14:36 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(07 Sep 2022 16:12 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(07 Sep 2022 17:42 UTC)
|
Re: One more review.
John Cowan
(07 Sep 2022 19:02 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(07 Sep 2022 20:55 UTC)
|
Re: One more review.
John Cowan
(07 Sep 2022 23:38 UTC)
|
Re: One more review.
Marc Nieper-Wißkirchen
(08 Sep 2022 05:56 UTC)
|
I have taken some time to review this srfi. Please, note that I am not a very experienced schemer, and this review is more about readability, typos and clarity for the ignorant, rather than about substantial things. I still hope that it can be useful. I used the org-mode format for representing issues, I hope it does not make finding the particular places in the srfi mentioned too inconvenient. * Remarks and comments ** 2 A continuation prompt is a special continuation frame Why are these tags that delimit continuations called "prompts"? I admit that a name it self does not mean much, but still, how is "incitement to action" (the definition of "prompt") connected to stack frames? ** 2 When continuations are captured, the list of captured continuation frames are always delimited at some continuation prompt. Shouldn't it be "delimited _by_ a continuation prompt"? ** 2 Finally, a primitive is provided that allows calling a thunk in a given continuation instead of just delivering values to it. Would it be possible to write the name of this primitive right here? ** 2 The exception mechanism of R6RS and R7RS is detailed with respect to the concepts introduced in this SRFI. I feel that "reinterpreted" would be a better word than "detailed" in this phrase. ** 2 The parameter object mechanism of SRFI 39 and is detailed with respect to the concepts introduced in this SRFI. Same suggestion. ** 2 at the latest by the default prompt at the start of each thread Why is "at the latest" used here? I understand the idea -- the continuation prompt is at the beginning of the thread, so effectively, even call/cc cannot "jump" above that. But how is it connected to "late"/"early"? ** 2 Moreover, the API is stripped by removing thread-terminate! I thought that srfis were expected to be append-only when finalised? I agree that "thread-terminate!" is not a very good primitive (especially with srfi-18 semantics), but I suspect that a PFN for srfi-18 would be a better choice than overriding it implicitly. Or, at least, a PFN to SRFI-18, saying that there is some other srfi that slightly changes the semantic would be nice to have. ** 3 Although a portable implementation is not possible, it is demonstrated by the sample implementation that a small set of primitives Could a link to the Section 6 be added at this place? ** 4 call/cc</code> allows capturing the current continuation and reifies it into a procedure that, when later called, abandons the then-current continuation and replaces it with the captured one May I ask that an illustration be added to this place of the SRFI? Even if an ASCII-art one? This place is important for understanding the whole concept. An illustration would make this easier. ** 4 With <code>call/cc</code> alone, the programmer has no control over the size of this end segment. For me, as a layman, it is not clear why this is a problem. After all, the "captured continuation" is just a pointer to the stack head (and the dynamic environment, I guess...). Why would the depth be of interest, if it is still just one or two pointers? ** 4.1.2 when a continuation (captured with call/cc) is called, the invocation never returns to the continuation of the call to captured continuation Replace with "to this call's continuation"? Otherwise it seems that these are two different continuations. ** 4.1.2 call-with-composable-composition I think, call-with-composable-continuation was intended. Additionally, would it be also abbreviated as call/cc ? Maybe rename to reduce ambiguity? ** 4.1.3 Continuation marks are a feature that allows attaching information in the form of key-value pairs to individual continuation frames, which are maximal segments of continuations of a number of active tail calls followed by at most one non-active tail call. I suggest defining a "continuation frame" first, and then defining "continuation marks". A "continuation frame" seems to be something very similar to a "stack frame", but not quite. Could this be clarified? What is an "active tail call" versus a "non-active tail call"? Why would tail calls even be registered in the call stack? In general, this paragraph is very difficult to understand. ** 4.1.3 Continuation marks can be used to implement exception handlers, parameter objects, and an implementation of delay and force of R5RS that supports iterative lazy algorithms. May I ask for intra-document links to the Sections that describe these features? ** 4.1.3 A crucial feature of continuation marks is that they allow one to attach marks to the most recent continuation frame without creating active non-tail calls. This allows one to implement a parameterize form where the last expression in its body is in tail context when the parameterize form is in tail position. Similarly, it becomes possible to implement with-exception-handler in a way so that its thunk is called in tail context if the call to with-exception-handler is in tail context. Is that not true with r7rs' `with-exception-handler`? If yes, I think that adding a note about that would be nice. ** 4.1.3 <p>In fact, with continuation marks, the concept of tail calls becomes operationally observable.</p> What does "operationally observable" mean? ** 4.1.4 For that, this specification extends the semantics What does this mean? "Concept A" is linked to "Concept B". For that, we extend? Maybe something like "in order to clarify how exactly these concepts' implementations would interact in a Scheme interpreter, we have to specify several implementation details of srfi-18" ** 4.1.4 this specification extends the semantics of SRFI 18 to provide consistent semantics "extend semantics to provide semantics" sounds a bit awkward. ** 4.1.5 As a definition of parameter objects in terms of continuation marks is possible, and as an implementation in terms of these "in terms of them"? (I am not a native speaker though, so this may be totally wrong. Just sounds a bit not smooth) ** 4.1.5 If a parameter that is inherited by a newly created thread and has not been reparameterized is mutated by one of the threads, the mutation will also (eventually) be observed by the other thread. Not a critique, but rather a question. Thread-shared data usually involves some synchronisation primitives. The new multithreading semantic does not liberate us from them, right? Are they going to be easier or harder to use with the new thread semantic? Also, is there a "recommended" way to make that "eventually" more certain? ** 4.1.6 Consequently, this specification defines <dfn>promises I am not sure "consequently" is the best word here... ** 4.3 SRFI 18 is extended Is there such a thing as "extending an srfi"? ** 4.4 call/cc is usually advertised as the hammer that can be used to implement all other control operations. In fact, this is not true for the undelimited continuations that the standard call/cc captures but for delimited continuations Slightly confusing grammar. What is exactly not true here? I would suggest something like: call/cc, which captures undelimited continuations, is usually advertised as the hammer that can be used to implement all other control operations. However, this is not true. Delimited continuations, on the other hand, can be used to implement all other control operations, and this SRFI properly introduces them in Scheme. ** 4.4 at the start of each thread. Including the primordial thread. ** 4.4 Consequently, this SRFI provides versions of exception handlers with improved tail-call guarantees. The issue is not academic as the right tail-call guarantees are crucial for writing iterative algorithms in Scheme. How are exceptions critical for iterative algorithms? I mean, I am just admitting my own incompetence here, but potential readers may also be missing context. After all, an algorithm may not use any exceptions at all. ** 5.2 Entry format What is an "entry"? ** 5.3 An iterator for the empty list returns <code>#f</code> and an iterator for the empty list. An iterator for a non-empty list returns the head of the list and an iterator for its tail. So, the iterators for (#f, '()), and for '() return the same two values? I am feeling an "off by one" error here. ** 5.4.2 Prompt Tags So, "Prompt Tags" 5.4.2 are defined before "Continuation Prompts" 5.4.3. That is like putting cart before the horse, no? ** 5.4.2 (default-continuation-prompt-tag) Is it intentional that different threads have identical (in the sense of eq?) initial continuation prompt tags? Wouldn't that lead to confusion? ** 5.4.3 <p>We identify a <dfn>continuation</dfn> with What does it mean "identify X with Y"? Did you mean "we define 'a continuation' as the call stack", or something like that? ** 5.4.3 a sequence of active procedure calls, beginning with the most recent call. What is an "active call"? Or, rather, what is an "inactive call"? ** 5.4.3 which are maximal subsequences of active procedure calls containing an arbitrary number of active tail calls followed by at most one active non-tail call. I find this confusing. What is a "sequence of active tail calls"? Aren't tail calls naturally disappearing from the call stack, so that there effectively is only one at any moment? ** 5.4.3 continuation prompt</dfn> is a type of continuation frame such that when values are delivered to a continuation extended by such a continuation frame, the values are effectively delivered to the non-extended continuation. So, a continuation frame is a sequence of active procedure calls. Let us imagine a call to (values (eval stuff)). The only thing the call to "values" does is deliver the values to the continuation. Is the call to "values" a continuation prompt then? ** 5.4.3 A continuation prompt with a given prompt tag is <dfn>available</dfn> in a continuation if I think that the definition of (continuation-prompt-available? <var>prompt-tag</var>) would have fit into this subsection better than into the one where it is defined now. ** 5.4.3 (call-with-continuation-prompt <var>thunk</var> <var>prompt-tag</var> <var>handler</var>) That is a bit of an aesthetic nitpick, but why is the "prompt-tag" the second argument rather than the first? This is just an opinion, but seeing a page of code that is tagged with "something", and then having to turn the page to see what is the actual tag name is not very ergonomic. ** 5.4.3 (call-with-continuation-prompt <var>thunk</var> <var>prompt-tag</var> <var>handler</var>) It is not specified whether this is a procedure or syntax. ** 5.4.3 (call-with-continuation-prompt <var>thunk</var> <var>prompt-tag</var> <var>handler</var>) Would a syntactic version be nice to have by default? I.e. #+begin_src scheme (with-continuation-prompt-tag ((make-continuation-prompt-tag)) (+ 1 2) (- 1 1) (values 'a 'b)) #+end_src ** 5.4.3 When the default handler is called, it reinstates the continuation prompt and calls <code><var>thunk</var></code> with no arguments in the resulting continuation.</p> What does "reinstate" mean? Do I understand correctly that the default handler "restarts" thunk over and over again? So, if someone "aborts to the most recent prompt", the code will run again? Or, in other words, that by default (with the default handler), (abort-current-continuation) results in an infinite loop? ** 5.4.3 (abort-current-continuation <var>prompt-tag</var> <var>obj</var> …) Also not specified whether this is a procedure or syntax. ** 5.4.3 is aborted and replaces them with a call to the handler recorded with the prompt tag with the arguments <code><var>obj</var></code> So, by default the (handler) is called with `thunk` as an argument, but it may be called with `objs` in stead, right? Intuitively, `objs` should be procedures, right? Is there any reasonable case when `objs` are not procedures? Also, the default (handler) accepts a single argument. What if I pass zero or two `obj`s? ** 5.4.3 when <code>abort-current-continuation</code> is called, due to the presence of <code>dynamic-wind</code> winders (see below) it can still happen during the aborting process that it becomes no longer available. You are pointing to the use of `dynamic-wind` below, but the example below does not use `dynamic-wind`. ** 5.4.3 (let ([tag (make-continuation-prompt-tag)]) Is is really useful to have a separate `make-continuation-prompt-tag`? Why not just `gensym`? Or even better, why not just use symbols as prompt tags? ** 5.5 also exports the condition types described in the previous section on continuation prompts. Does every sub-library of srfi 226 export those condition types? ** 5.5 <p>Each <dfn>continuation (procedure)</dfn> is a procedure.</p> What does this mean? Isn't a continuation a "sequence of active procedure calls"? ** 5.5 <p>If not supplied, <code><var>prompt-tag</var></code> defaults to the default prompt tag.</p> Is it true that if I only ever rely on the default prompt tag, I do not need to import (srfi 226 control prompts)? But if I actully supply the second argument to `call/cc`, I have to import it? ** 5.5 aborts all active procedure calls in the then-current continuation up to but not including a continuation prompt with <code><var>prompt-tag</var></code> or up to but not including an active procedure call shared by the current and captured continuations, whichever comes first. What does it mean "first"? From which side of the stack "first"? Or does it mean, "first in time"? What if the prompt with the tag appears _after_ the procedure is called, but _before_ the stack is reinstated? I.e. in the multithreaded case. What if the prompt with the tag occurs in the "unshared" branch of computation? ** 5.5 up to but not including _a_ continuation prompt So there may be _several_ continuation prompts with the same tag, right? Which one is selected? May I ask for an illustration here? ** 5.5 so the result of the second example is <code>7</code> times the value of the first.</p> I suspect that the example should use call-with-composable-continuation, rather than call-with-current-continuation. ** 5.5 enough to support the known delimited control operators. I do not know the delimited control operators. ** 5.5 which treats continuations as procedures for compatiblity reasons Compatibility with what? With Racket? Is there a better (but incompatible) way to treat continuations. ** 5.5 (call-with-continuation-barrier <var>thunk</var>) Just for clarity, what would (call-with-continuation-barrier values) do? ** 5.5 <p>The following example comes form <cite>R<sup>6</sup>RS</cite> and demonstrates jumping out of the post-thunk.</p> Not entirely related, but what do the `values` thunks to in this example? Are they there to "do nothing", or they have some meaning that I am missing? ** 5.6.1 (with-continuation-mark <span class="token">key-expr</span> <span class="token">val-expr</span> <span class="token">expression</span>) Why is `with-continuation-mark` a syntax, not a procedure accepting a thunk? ** 5.6.1 (call-with-immediate-continuation-mark <var>key</var> <var>proc</var> <var>obj</var>) What is the use-case for this procedure? Not a critique, but I am feeling that this procedure is here because there is some "most natural use case" for it. ** 5.6.1 continuation frames were separated by a continuation prompts I thing it should be "separated by a continuation prompt". ** 5.6.1 (continuation-mark-set->list <var>mark-set</var> <var>key</var> <var>prompt-tag</var>) It seems to me that the name of the function would better be (continuation-mark-set->list-of-values). I initially thought that it just converts the set of keys to the list off keys. ** 5.7 Conceptually, each continuation contains at least one otherwise inaccessible parameterization continuation mark, whose value is a parameterization. What is the reason for making it "otherwise inaccessible"? Would it not be better to agree upon some chosen `key`, and be able to obtain parameterizations with (continuation-mark-set->list) Also, isn't the (current-continuation-marks) function expected to return the parameterization continuation mark as well? ** 5.8 When values are delivered in the initial continuation, the program exits normally. What happens to the values delivered to the initial continuation? Are they expected to be the "exit status"es on POSIX systems? Or, maybe "objects" when run from PowerShell? ** 5.8 call-in-initial-continuation What is the use-case for this procedure? If I am running something in an "initial continuation", is it expected to be "almost like starting a subprocess"? What if I use call-in-initial-continuation in a non-primordial thread? Will it be run in the initial continuation of the program, or the thread? ** 5.9 Evaluates to a promise that behaves as follows when forced in a continuation: What does it mean "Forced in a continuation"? Everything in Scheme has a continuation, hasn't it? ** 5.9 Otherwise, <code><span class="token">body</span></code> is evaluated in an initial continuation. This sounds a little counter-intuitive? Why is it not evaluated in the continuation of the call to (delay)? ** 5.9 <cite>R<sup>7</sup>RS</cite> includes the description of a one-argument procedure also named <code>make-promise</code> that Maybe writing R<sup>7</sup>RS-small would be more correct there? ** 5.9 As the semantics of <code>make-promise</code> in <cite>R<sup>7</sup>RS</cite> makes I am not a native speaker, but I think it should be either "the semantics make" or "the semantic makes". I may be wrong though. ** 5.9 <cite>R<sup>7</sup>RS</cite> includes the unfortunate requirement that the body of a <code>delay</code> form has to be evaluated in the dynamic environment of the call to <code>force</code>. Again, I think that R7RS-small was really meant. ** 5.9 the correct semantics would have been to evaluate the body of a <code>delay</code> form in the parameterization of that form. I think it would have been better to say "more consistent" than "correct". Correctness usually means that the result is mathematically or logically true, however this is unrelated to the evaluation context. And, getting back to my remark four points above: it is written in this srfi that the "body" is evaluated in the "initial continuation". Since parameterization is just a continuation mark, it seems that the document is self-contradictory. <body> should be evaluated either in the continuation of (delay), or (force), or the initial continuation, and the parameterization, being a property of the continuation, would follow from it naturally. ** 5.9 In fact, with regard to the dynamic environment, the semantics of promises are now equivalent to that of threads. Would it make sense to say that the scheme system may start evaluating the <body> of (delay) right after the call to (delay) in a different thread, if there are unused computational resources? ** 5.10 Conceptually, each continuation contains at least one otherwise inaccessible exception handler stack continuation mark, whose value is a list of exception handlers, which are one-argument procedures. Would (current-continuation-marks) be able to access this continuation mark? If not, and if the parameterization should also belong to the "inaccessible" continuation marks, would it make sense to write something on distinguishing "accessible continuation marks", "inaccessible continuation marks"? ** 5.10 The most recent exception handler is <dfn>removed</dfn> in a continuation by recording the exception handler stack of the continuation, constructing a new exception handler stack by removing the first element of the recorded exception handler stack, and annotating the continuation with an exception handler stack continuation mark whose value is the new exception handler stack.</p> When are exception handlers _removed_? I am aware of (with-exception-handler) and (guard), but I haven't seen (with-last-exception-handler-removed). ** 5.10 Returns a newly allocated list containing the handlers of the current exception handler stack, with the most recently installed handlers coming first. Why does it have to be "newly allocated"? What is wrong with returning literally the value of the exception handler stack continuation mark? ** 5.10 current-exception-handlers, current-exception-handler The two names differ by a single character. It's just a nitpick, but I think that it is easy to make a mistake here. ** 5.10 Raises a <dfn>non-continuable exception</dfn> as follows: What is the need to have non-continuable exceptions? After all, why not just let the handler decide whether it wants to continue after an exceptions. ** 5.10 the current continuation is instead aborted to the most recent continuation prompt tagged with the default continuation prompt, and its handler invoked on a thunk that, when called, 1. Did you mean tagged with the default continuation prompt _tag_ ? 2. Maybe I am mistaken here, but isn't the default handler a "restarting handler"? (The one that is just calling the `thunk`). Seems like an easy way to get unexpected infinite loops. ** 5.10 The tail context requirement is neither in <cite>R<sup>6</sup>RS</cite>, nor in <cite>R<sup>7</sup>RS</cite>. -small ? ** 5.10 (guard Why do we need to have both (guard), and (with-exception-handler)? ** 5.10 Installs an exception handler as described below Was "as described above" intended? ** 5.10 the recorded continuation is reinstated with the most recent exception handler removed and a continuable exception with <code>obj</code> is raised in the resulting continuation What is `obj`? I do not think it is mentioned in the definition of (guard). ** 5.10 Otherwise, the resulting values of the equivalent <code>cond</code> expression are delivered to the aborted continuation. What if a non-continuable exception is raised? ** 5.10 delivered to the aborted continuation. This sounds strange. If it is aborted, now can anything be delivered to it? It might have already been garbage-collected. ** 5.11 also exports the following procedures from SRFI 18 Not sure it is a viable critique, but just reexporting functions from a different srfi sounds strange. Would it be better to propose a PFN to srfi18, which would say that "if srfi226 is supported, tread semantic should be extended as described there"? Not sure it is a good idea though. Maybe a separate srfi here would really be appropriate. "On compatibility between srfi 18 and srfi 226" ** 5.11.2 When values are delivered to the initial continuation, the values are stored in the <code>end-result</code> of the thread before the thread is finally terminated.</p> What is the "end-result"? Is it defined in srfi-18? ** 5.11.2 As threads inherit the parameterization of when they are created When I run a thread created with `make-thread`, it will by default have the creation-time parameterizaition. But when I run #+begin_src scheme (let ((a (thread ...))) (parameterize ((p 1)) (thread-start! a))) #+end_src Will the parameterization be the old one or the new one? ** Postamble Many of my "comments" are probably very lame, as I am not an experienced schemer, and largely out of context, but I hope they may be useful still. In general, it is a great and comprehensive work, I hope that method described in it would be widely adopted. -- Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop)