Subject: bug#46009: exception from inside false-if-exception?

From: Maxime Devos <[email-masked]>
Date: Wed, 19 Jun 2024 18:51:00 +0200

I don’t think we are going to agree on much here, but some new points of agreement pop up occassionally. Continuing this …

>> What is lousy and expectation-violating about a keyword argument
>> doing what it name describes?
>well, this should have been discussed at the R6RS committee, but...

This is not something to be discussed at the R6RS committee, because #:unwind? is a Guile extension, not part of R6RS. Unless the idea is to (in an alternate past) have introduced keyword arguments to R6RS and this is about keyword argument stuff.

>an API is facilitating efficient reading comprehension when the "verbs" are closer to the beginning of the "sentence" (the sexp), and nothing later in the sentence adjusts the meaning of the verb fundamentally.

Do you have evidence for this? I mean, beyond your own experience, anecdotal evidence doesn’t generalise well. (Not that I don’t use anecdotal evidence or the like myself, but it would be better if at least one of us has something better.)

Also, I don’t see a fundamental change in meaning, except in the sense that anything could be considered fundamental, just like every two topics are related however distantly, etc..  #:unwind? #true/#false is too small a change for me to consider ‘fundamental’ – both #true?/#false result in exception handling, just slightly different exception handling. Neither do I really have an argument to consider it un-fundamental, size isn’t everything. In this context, it’s too meaningless a label to base things on or me.

Also, I disagree – this is not a garden path sentence situation (where, and OV (object-verb) languages exist. If you want to understand a sentence just read the entire sentence?

Now, I did some quick searches for differences in reading comprehension for OV/VO languages, and unfortunately found nothing relevant (neither positive nor negative nor neutral).

If we are talking about reading comprehension, I would like to point out that (Guile) Scheme is case-sensitive. I’d consider it beneficial to reading comprehension if the procedure names that are written in documentation correspond to what’s recognised by the implementation. Someone coming from case-insensitive languages and someone unfamiliar with the upcasing practice might get confused for a moment.

> and especially not a keyword arg at the end of the sentence that is not even mandatory, and defaults to one of the rather different possibilities.

Also, I would rather not have the keyword argument at the front. Usually keyword arguments aren’t verbs and better fit for the end of the procedure call, and even if they were sometimes better suited for the beginning, you now have a situation where sometimes keyword arguments are at the front and other times are at the end, which creates ambiguity when interpreting the arguments.

Another interpretation of this would be to replace the keyword argument by an optional argument, but optional arguments are at the end, so same thing.

A potential way to avoid this is to split things off into a new procedure, say with-exception-handler/unwinding or with-exception-handler/winding (well not these precise name because of issues mentioned earlier). While both provide a similar function (to the point that often you can swap between the two without trouble), if you were to precisely document them or implement them, the documentation and implementation would be rather different, so I think this would be better than an argument (whether (non-)keyword or optional or required) (it’s not like the unwinding-ness varies dynamically in practice, so not really a point in sharing a procedure name).

(I don’t think this really is an improvement in readability though, just a neatness thing.)

>in this case, it's a primitive to catch and handle exceptions. and it's possible to implement both #:unwind? #t and #f so that when the handler returns then the control flow continues at the guard, and never at the RAISE. it's just that one version would call the handler before unwinding.

AFAICT this paragraph as-written is technically true, but rather misleading – there is also raise-continuable, where the control flow needs to continue at raise-continuable.

>> If you have unwinded, it’s too late to return return from the raise
>> (unless the implementation is doing delimited continuations
>> shenanigans, which maybe you had in mind?), which explains the
>> behaviour for #:unwind #true.
>
>i didn't have anything on my mind, and that's the point. i'm simply new to scheme. >now that i've learned it, i'll learn to live with it.

[no replies to this part, I just don’t want to trim context / disrupt flow here]

>put another way, my learning curve would have been much steeper if the two, >rather different behaviors were defined under two distinct names (or at least >mentioned with bold in the documentation).

At first, I was going to write that you probably meant to write the opposite that if the different behaviour were more clearly different (e.g. with distinct names), then the learning would be _less_ steep, but now I read (Wikipedia article on learning curves):

>The common expression "a steep learning curve" is a misnomer suggesting that an
> activity is difficult to learn and that expending much effort does not increase
> proficiency by much, although a learning curve with a steep start actually represents
> rapid progress.[2][3]

(reading comprehension failure).

>> That said, I would prefer it to be named something like [#:handler-context 'raise]/[#:handler-context 'guard]

>that would be better, but i still wouldn't like the fact that it's focusing on the dynamic context of the handler, instead of the different flow of control.

I don’t get the distinction. These are two sides of the same coin? Almost equivalent, though I suppose that flow of control is a bit more general since if you only have info on the dynamic context then multiple flow of controls remain possible (but I don’t know any important change of flow that’s still correct exception handling).

Also, for a hypothetical #: [something flowy], I don’t like the fact that it’s focusing on the flow of control instead of the dynamic context of the handler.

Whenever I do #:unwind? #false/#:true (#:handler-context ‘raise’), it’s to get the dynamic environment/context (I forgot standard terminology) right (think call stack and parameter bindings (either at raise or guard, depending on what you are implementing)). I don’t really care about Guile does the control flow is implemented, as long as the dynamic environment is correct and raise-continuable functions (though TBH I almost never use raise-continuable, and when I do, I always think something along the lines of “neat that this is possible, but there are other methods that are simpler both to implement (whether caller/callee) and to understand”).

>for reference, in CL they are called HANDLER-CASE and HANDLER-BIND, with completely different syntaxes.

Can’t honestly say I like those names (those suffixes -CASE / -BIND don’t really provide information, you have to go into the spec of HANDLER-CASE / HANDLER-BIND itself to get the differences).

Going by this and some earlier text, it seems we are in agreement that with-exception-handler should be split (albeit for different reasons).

>> Wait where did this happen? You say what’s happening, but you don’t
>> seem to be referring to false-if-exception stuff, and you didn’t
>> mention continuation barriers earlier.

>this has caused me layers of confusion, which is sadly reflected in my mails.

>guile prints a backtrace without any prelude, which made me think that it's my own code that is printing this backtrace and exception in the logs (remember, i'm working on nested error handling and logging in shepherd). then 3.0.9 does something funny with the control flow, which further made me believe that i'm logging an exception coming from inside FALSE-IF-EXCEPTION and it's somehow flying past my error handler, and reaching fibers.
>
>and i think i'm still confused about a possible continuation barrier being injected somewhere that probably/maybe causes the exception from inside FALSE-IF-EXCEPTION somehow ending up at fibers instead of my error handler. but at this point i'm reaching the frontier of my understanding of scheme, delimited continuations, fibers, etc.
>
>so, one step at a time. once this test.scm is clear, and i'm able to run shepherd on guile-next, then i'll get back to investigate my original issue in its original context.

Maybe you can install a breakpoint on scm_c_with_continuation_barrier or something, and see when this happens. Or search for with-continuation-barror in Scheme code. I don’t think Shepherd has much use of with-continuation-barrier. If it does, it’s a rather crude tool and can probably be replaced by something else that doesn’t replace the exception handler.

I don’t know what you are investigating precisely, but maybe it is

>https://sources.debian.org/src/guix/1.4.0-6/gnu/build/shepherd.scm/?hl=112#L109

If it is this, surely (@ (fibers) sleep) could be adjusted to _not_ suspend (and instead just, well, sleep) when there is no Fibers task scheduler (similar stuff exists for the (Fibers) channels and conditions implementation).

Best regards,
Maxime Devos.