> Also, I don’t see a fundamental change in meaning
>>just look at my test.scm:

>>without the explicit (let/ec return ...) wrapper, and the explicit use of 
>>(return ...) in the handlers, the two invocations lead to different flow of 
>>control.
>>It's icing on the cake that if the underlying ERROR were in fact a 
>>RAISE-CONTINUABLE, then >it wouldn't even lead to a &NON-CONTINUABLE error in 
>>the #:unwind? #f case, only to a >different return value (different flow of 
>>control).

‘error’ is a procedure, not something that would be raise-continuable (I guess 
you could pass it to ‘raise-continuable’, but seems rather unlikely). I propose 
replacing ERROR by condition (lack of quotes and capitals intentional, since 
I’m referring to the concept here, not a Scheme object or macro and in 
particular not the macro ‘condition’ from SRFI-35

Now I look at the output (somehow when I previously read it I thought it was 
testing control flow with dynamic-wind, but it turns out it is testing 
semantics instead (without dynamic-wind) – reading comprehension failure on my 
part?), the observed behaviour (for v3.0.9) is indeed a significant change.

I don’t think test.scm is a good example though – the output is, AFAICT, just a 
bug – for the nested error, AFAICT neither of the exception handlers receive 
the exception, which is plain nonsense.

>>In my vocabulary, a different flow of control when using a control-flow 
>>primitive is a >>fundamental change to its meaning. but your miliage may wary 
>>of course.

It’s not in mine. What matters to me is (denotational) semantics (in a broad 
sense, including things like complexity, whether something is constant-time 
(important for cryptography), etc.). Control-flow is more a operational 
semantics thing. Often a convenient way of defining the semantics of control 
structures, but ultimately, if with a different control flow you achieve the 
same semantics (and aren’t compiling with the equivalent of -Og), that’s fine 
too.

>> If we are talking about reading comprehension, I would like to point
>> out that (Guile) Scheme is case-sensitive.
>for effective communication we need a syntax to designate when a word is to be
> interpreted in the scheme/lisp domain (as opposed to plain english). using 
> all-caps
> in english prose is a somewhat well-spread syntax for that, even in non-CL 
> lisp
> contexts in my experience.

A simple syntax is to use quotes, e.g. ‘call-with-current-continuation’, which 
is well-spread (even outside programming to some degree), and does not require 
mangling the casing. Depending on the medium, there are other options like 
monotype, putting a small box around it or coloring. For texts (but not e-mail 
or terminal things), I recommend monotype (and depending on capabilities of the 
writing software and aesthetic preferences, maybe try out things like coloring 
and boxes).

So …

>> Someone coming from case-insensitive languages and someone
> unfamiliar with the upcasing practice might get confused for a
> moment.
>for a moment. and that is key.

… why not both eliminate the moment of confusion _and_ use a syntax that avoids 
the ambiguity?

>> >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?
>
>not really, because there are two, orthogonal dimensions here:
>
>1) whether to unwind the stack,
>
>2) where to return the control flow when the handler returns.
>
>(even if one combination is impossible)

Err, all four combinations are possible with sufficient application of 
delimited continuations. If you have unwinded you can just rewind (if you have 
captured continuation things and accept the change in visible behaviour to 
‘dynamic-wind’, the first is not a given because of performance considérations 
and the latter isn’t a given because, well, different behaviour).

There are two components to (1):

   (1)(a): what does dynamic-wind see?
   (1)(b): how does unwinding affect the dynamic environment of the handler.

For (2): first, there is no ‘return the control flow’ happening! The returning 
is all in tail position (albeit with some extra layers in the dynamic 
environment, so don’t do tail loops with with-exception-handler or you will use 
lots of memory) – according to the docs, once the handler is invoked, 
with-exception-handler stops doing things.

As mentioned in the documentation:

> https://www.gnu.org/software/guile/manual/html_node/Raising-and-Handling-Exceptions.html
>Unless with-exception-handler was invoked with #:unwind? #t, exception 
>handlers are invoked __within the continuation of the error__, without 
>unwinding the stack. The dynamic environment of the handler call will be that 
>of the raise-exception call, with the difference that the current exception 
>handler will be “unwound” to the \"outer\" handler (the one that was in place 
>when the corresponding with-exception-handler was called).
> […]
> if with-exception-handler was invoked with #:unwind? #t is true, 
>raise-exception will first unwind the stack by invoking an escape continuation 
>(see call/ec), and then invoke the handler __with the continuation of the 
>with-exception-handler call__.

(emphasis added with __)

Now, while ‘return the control flow’ is not accurate, there is definitely some 
control flow manipulation going on (see: ‘call/ec’). But consider this: whether 
you are in tail-position w.r.t. something, is a property of the dynamic 
environment! I mean, it definitely is visible in backtraces (which you can make 
with ‘(backtrace)’, doesn’t need exceptions).

Let’s look at some R6RS definitions :

>For a procedure call, the time between when it is initiated and when it 
>returns is called its dynamic extent. [currently irrelevant things about 
>call/cc]
>Some operations described in the report acquire information in addition to 
>their explicit arguments from the dynamic environment.

(Technically it doesn’t state that the concept of ‘dynamic environment’ 
includes the ‘dynamic extent’, but it seems quite natural to consider all the 
dynamicness together, and ‘environment’ seems a sufficiently generic word to 
also include the ‘extent’.)

(dynamic extent <-> a region of stack, without focus on what the dynamic 
bindings in that stack are, dynamic environment <-> the full stack upto a 
point, with a focus at what’s visible at the point]

Summarised: stack stuff is part of the dynamic environment, control flow is 
stack stuff, so control flow is dynamic environment stuff.

>> >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).

>my point is not whether it's deducible, or whether the names are good. my 
>point is that one API is misleading (i.e. wastes human time), while the other 
>forces the reader to, at worst, look up the documentation. but certainly not 
>conflate the two, thinking that they are doing the same thing -- plus some 
>implementation detail regarding unwinding that he can postpone learning about.

It wasn’t my point either, I didn’t think you liked them either, I didn’t think 
your point was whether it’s deducible, I just interpreted it as an example of 
splitting stuff up and wanted to make clear those don’t seem good names.

>here's a caricature of this situation:
>
>imagine a hypothetical (raise [a potentially larger sexp] #:enabled? #t/#f) 
>control flow primitive that simply returns when #:enabled? is #f.
>
>one can argue that "well, it's disabled, dummy!" (turing tarpit), or try to 
>walk in the shoes of the reader or a newcomer.
>
>yes, you're right when you say something is possible, or deducible. but no, 
>with all due respect, you're wrong when you imply that it doesn't make a 
>difference (in human-brain efficiency).

With all due respect, that’s a strawman (as expected by a caricature).

I never claimed #:unwind? is a great name, that keyword #:enabled? is terribly 
named and even worse than #:disable, both you and I previously made clear that 
naming is important, I proposed renamings of #:unwind? to other things (that 
you even agreed to were better) or proposals for names for splitting up the 
procedure (not great names, but still more descriptive than #:enabled? Of all 
things).

Also, let’s do this walking into the shoes of the newcomer. As a newcomer, one 
of the things I do is _reading the documentation_ (I have heard this is a rare 
thing, but I actually do this, and did this).

As a newcomer, I find these references to delimited continuations, dynamic 
environment and tail call thingies, really interesting and cool, so I read a 
lot about it (more than I actually use them, even!) and try it out a bit. I 
also see these mentions of virtual machines and stack frames. I read those too 
of course, how stack frames are done is really cool and interesting (VM a bit 
less so, but still ok to skim through).

As a reader, I also think that sometimes the descriptions aren’t done well – 
it’s not a verbosity thing despite what some would say, but more a structure 
thing (both local or global). But that’s a property of the documentation, not 
the API.

I also thing that occassionally the APIs aren’t quite right, making it 
difficult to understand when to, say, use #:unwind? #false/#true. But as a 
newcomer, I don’t _need_ to know those distinctions, I can just assume the 
default is fine and avoid doing fancy dynamic environment stuff inside the 
exception handler. So, I can just avoid the complicated unwinding-or-not stuff 
until I’m not a newcomer anymore, and once that’s the case, I can start 
thinking about how to improve API stuff and utilise the fancy stuff.

Going back to the strawman:

> you're wrong when you imply that it doesn't make a difference (in human-brain 
> efficiency).

I didn’t imply this! Just because I said you need to _read the documentation_ 
doesn’t mean that naming and the like can’t be improved, the existence of a 
‘problem between monitor and chair’ does not exclude the existence of a problem 
in the software as well. Also that the status quo is ok doesn’t mean it can’t 
be be improved.

(I think the _name_ itself is fine, rather, I think that the _concept_ that it 
represents would preferably be changed to something different (and naturally 
this change in concept induces a change in name and perhaps semantics as well).)

>> Maybe you can install a breakpoint on
>> scm_c_with_continuation_barrier or something
>
>it assumes that it's already known that the curlpit is w-c-b, but that was 
>already well >into the debugging effort. see my attached patch that intends to 
>greatly shorten the >debugging time for anyone else walking down this path 
>after me.
>
>and it's been decades i've done any serious C coding, and i'm not looking 
>forward to >learning the ins and outs of gdb when i'm debugging an issue in 
>scheme.

Of course it assumes that the culprit is ‘with-continuation-barrier’. You 
provided the information that ‘with-continuation-barrier’ might have something 
to do with the issue to me. Also, I didn’t mention gdb anywhere. It’s a common 
debugger, but not the only one. And neither did I mention learning the ins and 
outs – setting up a breakpoint and asking gdb to print a backtrace is hardly 
‘the ins and outs’. (Whether the information of the backtrace will prove useful 
is another matter.)

Also, difficulty with C debugging is your problem, not mine – this thread isn’t 
named “how to simplify debugging from C”, as a volunteer I’m just mentioning 
some options to consider and I’m not your boss. Besides, given that the 
existence of FFI and the like, and that Guile it partially implemented in C 
instead of Scheme, it isn’t a given that the issue you are debugging is, in 
fact, fully in Scheme.

(Also maybe look if Shepherd is doing REPL stuff. The REPL implementation in 
Guile installs (in Scheme!) a continuation barrier.)

> I don’t think Shepherd has much use of with-continuation-barrier.
>
> IIUC, any time the posix thread enters C code is an implicit continuation 
> barrier. if a
> fiber in shepherd uses any primitive in guile that is implemented in C, then 
> it's a
> continuation barrier (and breaks when an attempt is made to suspend that 
> fiber).

Not in the sense of with-continuation-barrier. C code can throw Scheme 
exceptions and those exception can in turn be caught by normal Scheme exception 
(but no raise-continuable stuff from C).

On a related note, there is also some (non-Scheme) dynwind-rewind stuff in 
Guile’s C code. I don’t have a clue on how that works and if it even works at 
all.

> changing shepherd not to unconditionally unwind (so that meaningful backtraces
> can be logged) can lead to subtle issues due to now having some extra C 
> frames on
> the stack that were previously unwound prior to handling the error.

That’s not unwinding, that’s rewinding. Unwinding can’t lead to leaving extra C 
frames on the stack, because removing those C frames is part of the unwinding 
process and C frames are only generated by C code.  (Except perhaps for that 
dynwind-rewind stuff, but if it does that stuff, presumably it is prepared to 
deal with rewinding.)

Hypothetically a Scheme implementation could put C frames back onto the stack, 
but Guile doesn’t do this – whenever this would happen, it simply refuses(*). 
(IIRC it does so by throwing an exception.). From the documentation:

(*) besides perhaps that dynwind-rewind stuff.

>Scheme Procedure: suspendable-continuation? tag
>Return #t if a call to abort-to-prompt with the prompt tag tag would produce a 
>>delimited continuation that could be resumed later.
>
>Almost all continuations have this property. The exception is where some code 
>between the call-with-prompt and the abort-to-prompt recursed through C for 
>some reason, the abort-to-prompt will succeed but any attempt to resume the 
>continuation (by calling it) would fail. This is because composing a saved 
>continuation with the current continuation involves relocating the stack 
>frames that were saved from the old stack onto a (possibly) new position on 
>the new stack, and Guile can only do this for stack frames that it created for 
>Scheme code, not stack frames created by the C compiler. It’s a bit gnarly but 
>if you stick with Scheme, you won’t have any problem.
>
>If no prompt is found with the given tag, this procedure just returns #f.

Best regards,
Maxime Devos.

Reply via email to