* call/cc and recursive vm invocations @ 2010-03-04 19:54 Andy Wingo 2010-03-06 17:13 ` Neil Jerram 2010-03-06 23:23 ` Ludovic Courtès 0 siblings, 2 replies; 8+ messages in thread From: Andy Wingo @ 2010-03-04 19:54 UTC (permalink / raw) To: guile-devel Hey folks, One of the last missing features / regressions on my list for 2.0 is to fix make-stack and start-stack to work with the VM. In case you haven't noticed yet, if you get an error at the REPL and ask for a backtrace from the debugger, you get not only the frames that pertain to your own computation, but also frames from the REPL implementation itself. That's not good, and it's a regression. The solution is to implement start-stack for the VM. Prompts provide an ideal set of primitives. So start-stack can be this: (define (start-stack tag exp) (let ((prompt-tag (fresh-tag))) (with-fluids ((stacks (acons tag prompt-tag (fluid-ref stacks)))) (% prompt-tag exp (lambda (k . args) (apply values k args)))))) This assumes some fluid is around named "stacks". Then we can implement make-stack: (define (make-stack tag . narrowing) (let ((prompt-tag (assq-ref (fluid-ref stacks) tag))) (unless prompt-tag (error "prompt tag not found quoi")) (narrow-stack (continuation->stack (call/cc (lambda (k) k) prompt-tag)) narrowing))) And this assumes that we factor out stack narrowing to its own procedure, and that continuation->stack exists (it does now, inside scm_make_stack)... ...and, that we have a call/cc that takes two arguments. Now, currently "full" continuations integrate just fine with delimited continuations. (Recall that full continuations copy the whole stack, both on the C and Scheme level.) But here we'd like the ability to capture just the Scheme stack, and only up to a certain delimiter (prompt). In fact this actually adds more power to call/cc, by allowing the user to delimit the scope of the continuations that it captures (via prompts). However it seems that one cannot implement call/cc purely in terms of prompt and abort -- because with prompt and abort, reifying a continuation only happens on an abort, which unwinds the dynamic state. Likewise invoking a "full" (but delimited) continuation needs to replace the continuation from a prompt, but might not need to unwind all the way to the prompt, if the current and future continuations share part of their dynamic extents. Finally, delimiting call/cc makes it more expressive when invoked from a callback API. Imagine you have some inversion-of-control library that calls your code every so often -- currently in Guile you can't capture the continuation in one callback, then invoke it in the next, because it means your first callback would return twice. Well, truth is you can, but often callback-style APIs are implemented in C or such, and often such code can't handle multiple returns. Reinstating a full-but-delimited continuation, on the other hand, can happen in different dynamic contexts -- each callback can set up a prompt, and calling a continuation with a specific prompt tag will overwrite the stack only up to the prompt with that tag. Relatedly, delimiting the continuations makes it easier to think about shipping them across the network. * * * So, that's the upside. The downside of delimiting "full" continuations is that you can't capture the C stack. This has a number of ramifications, but first, a little eulogy for Guile's current implementation of "full" continuations: it has been nice to be able to capture the C stack. It makes C coding more like Scheme coding, to know that control may flow through your functions in more than one way. I enjoyed it. But of course, to each implementation its own time, and this approach to continuations has been nearing its expiration date for years now. Most C code can't deal with nonlocal exits, let alone multiple returns. So the utility of this construct has been limited. Now, with the VM and partial continuations, we have the ability to capture the Scheme stack apart from the C stack, and more and more can be done within the VM itself. Call/cc is mostly useful in a pure Scheme context anyway, so it's natural to start thinking of an implementation of continuations that does not capture the C stack. (FWIW, the ability to exit from C code nonlocally is key to Guile's C API, and I don't see a move away from that in any foreseeable future. The issue at hand here is *capture* of the C stack.) Anyway, enough about that. Practically speaking, not capturing the C stack means that you cannot invoke a continuation that was captured outside the current VM invocation. For example, there are a number of primitive functions that end up calling scm_call_1 or such, like scm_map. So this wouldn't work: (let ((k (call/cc (lambda (k) k)))) (map (lambda (x) (k x)) list-of-things)) because the lambda gets called within a recursive VM invocation, so there are intermediate C frames on the stack, so the continuation cannot be invoked. (The code to detect this exists already.) I did an informal experiment to see what code caused recursive VM invocations during a repl startup. The total was about 3500 VM invocations, most of those singly-recursive. I don't think this is expensive, but it's a bit unnecessary. The most common causes for recursive incovations for a repl startup were, in no order: primitive-load load-compiled/vm primitive-load-path call-with-input-string (only once or so) map for-each hash-for-each scm_eval_closure_lookup (?) scm_thunk_p (calls program's meta-thunk to get arity; wasteful GC-wise) scm_resolve_module (calls Scheme resolve-module) filter Note that dynamic-wind and with-fluids are no longer on there, thankyouverymuch. thunk? is only on the list because of the dynamic-wind invocations that are there, many of which could be replaced by with-fluids I think. (We should expose the current ports as fluids I think.) Map and for-each are interesting cases, actually: http://www.r6rs.org/formal-comments/comment-36.txt http://lists.r6rs.org/pipermail/r6rs-discuss/2007-March/001956.html But regardless of that, I think they should be in Scheme for purposes of inlining. (map (lambda (x) ...) '(list of few things)) should be inlined, and (for-each (lambda (x) ...) l) should always be inlined, because it allows further optimizations. * * * Practically speaking... I think I can delimit call/cc with not much work (perhaps tomorrow). But it is a visible change (if you're looking), so I wanted to put this mail out there to get comments. I had thought this was a 2.2 feature, but given the make-stack implications, I'm thinking it's a 1.9.9 feature. Reactions? Happy hacking, Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-04 19:54 call/cc and recursive vm invocations Andy Wingo @ 2010-03-06 17:13 ` Neil Jerram 2010-03-07 13:13 ` Andy Wingo 2010-03-06 23:23 ` Ludovic Courtès 1 sibling, 1 reply; 8+ messages in thread From: Neil Jerram @ 2010-03-06 17:13 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Andy Wingo <wingo@pobox.com> writes: > Hey folks, > > One of the last missing features / regressions on my list for 2.0 is to > fix make-stack and start-stack to work with the VM. Yes, I'd noticed that. > The solution is to implement start-stack for the VM. Prompts provide an > ideal set of primitives. Really? I'm anticipating some of what you've written below, here, but isn't using prompts and continuations, and maybe completely changing how continuations can be used, a bit of a sledgehammer, when all we want to do is tell `backtrace' to limit the set of frames that it prints out? > (define (make-stack tag . narrowing) > (let ((prompt-tag (assq-ref (fluid-ref stacks) tag))) > (unless prompt-tag > (error "prompt tag not found quoi")) > (narrow-stack > (continuation->stack > (call/cc > (lambda (k) k) > prompt-tag)) > narrowing))) > > And this assumes that we factor out stack narrowing to its own > procedure, and that continuation->stack exists (it does now, inside > scm_make_stack)... > > ...and, that we have a call/cc that takes two arguments. But, instead of requiring call/cc to change, couldn't we also write a new primitive equivalent to the combination: (continuation->stack (call/cc identity prompt-tag)) (Actually this sounds pretty similar to the current scm_make_stack, with an added search for prompt-tag.) > Now, currently "full" continuations integrate just fine with delimited > continuations. (Recall that full continuations copy the whole stack, > both on the C and Scheme level.) Do you mean in theory, or in current Guile? I'm afraid I'm not up to speed with where things have reached. <pause to check code...> OK, so it looks like we now have abort and prompt as primitives, and catch, throw etc. implemented in terms of them, but that continuations are thus far unchanged from their old form. Is that correct? I think this means that the idea of composable continuations, as discussed in your blog, is still pending. Also, if I'm understanding correctly, it seems to me that that idea is the same as having a 2 arg call/cc. If so, I think your email isn't proposing any new concepts beyond what you raised before, but rather highlighting an implication of implementing those concepts. Is that all correct? > In fact this actually adds more power to call/cc, by allowing the user > to delimit the scope of the continuations that it captures (via > prompts). Agreed - but this is just repeating what you've said before, isn't it? > Finally, delimiting call/cc makes it more expressive when invoked from a > callback API. Imagine you have some inversion-of-control library that > calls your code every so often -- currently in Guile you can't capture > the continuation in one callback, then invoke it in the next, because it > means your first callback would return twice. Well, truth is you can, > but often callback-style APIs are implemented in C or such, and often > such code can't handle multiple returns. Yes, I've hit this issue. It can be solved though, with Guile 1.8.x. > Relatedly, delimiting the continuations makes it easier to think about > shipping them across the network. Interesting. > * * * > > So, that's the upside. The downside of delimiting "full" continuations > is that you can't capture the C stack. Why? Also I'm not sure what "full" means here. > * * * > > Practically speaking... I think I can delimit call/cc with not much work > (perhaps tomorrow). But it is a visible change (if you're looking), so I > wanted to put this mail out there to get comments. Well, I hope that some of mine are useful. I'm afraid I don't yet understand well enough (despite all of the explanation above) what the impact of this visible change would be; perhaps some more concrete examples would help. But it seems a very big change, if it's only to produce delimited backtraces. > I had thought this > was a 2.2 feature, but given the make-stack implications, I'm thinking > it's a 1.9.9 feature. Reactions? My feeling is to do something smaller to get make-stack working for 2.0, and look at the wider change for 2.2. But I'm far from sure about that. Also, is there any possibility of keeping both old- and new-style continuations side by side? > Happy hacking, > > Andy Regards, Neil ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-06 17:13 ` Neil Jerram @ 2010-03-07 13:13 ` Andy Wingo 0 siblings, 0 replies; 8+ messages in thread From: Andy Wingo @ 2010-03-07 13:13 UTC (permalink / raw) To: Neil Jerram; +Cc: guile-devel Hi Neil, Thanks for looking at this! On Sat 06 Mar 2010 18:13, Neil Jerram <neil@ossau.uklinux.net> writes: > Andy Wingo <wingo@pobox.com> writes: > >> Hey folks, >> >> One of the last missing features / regressions on my list for 2.0 is to >> fix make-stack and start-stack to work with the VM. > > Yes, I'd noticed that. > >> The solution is to implement start-stack for the VM. Prompts provide an >> ideal set of primitives. > > Really? Yes, because prompts allow you to correlate function-call frames with the dynamic environment (wind stack) -- because a prompt captures the frame pointer, stack pointer, etc. >> ...and, that we have a call/cc that takes two arguments. > > But, instead of requiring call/cc to change, couldn't we also write a > new primitive equivalent to the combination: > > (continuation->stack (call/cc identity prompt-tag)) > > (Actually this sounds pretty similar to the current scm_make_stack, with > an added search for prompt-tag.) Good point! Perhaps this is the solution, then. Note that if we used call/cc in this way, we wouldn't be requiring call/cc to change, we would just be adding an optional second argument. >> Now, currently "full" continuations integrate just fine with delimited >> continuations. (Recall that full continuations copy the whole stack, >> both on the C and Scheme level.) > > Do you mean in theory, or in current Guile? I'm afraid I'm not up to > speed with where things have reached. It is true that some of my thinking on this was a bit muddy, and that muddiness translated to the explanation. So let me try again. I mentioned at some point that the PLT people prefer to speak of "composable continuations" versus "non-composable continuations", and I wasn't sure why. Now I know, I think: continuations captured by prompt/abort can be invoked for a value, so you can have continuations invoking continuations for a value -- the continuations can be composed together. On the other hand, continuations captured by call/cc are not composable. When you invoke a non-composable continuation, your continuation is trashed, and replaced with the one you invoked. When I said "full" in that mail, I meant "non-composable". So that's one axis. The other axis is delimited versus undelimited, and perhaps I have been able to convey this difference in the past. But in my earlier posts I had been lumping "delimited" together with "composable", and that's not necessarily the case -- you can also make a call/cc that is delimited, and that's what this mail was about. Guile's current call/cc is both non-composable, by nature (RnRS), and undelimited, by accident. It captures the whole continuation, regardless of prompts; and invoking a captured continuation reinstates the entire continuation, instead of reinstating just to a prompt. Call/cc is not implementable in terms of prompt and abort, because with prompt and abort, the continuation is captured only on abort, and that unwinds the dynamic state to the prompt. But (call/cc identity) implies no unwinding. > OK, so it looks like we now have abort and prompt as primitives, and > catch, throw etc. implemented in terms of them, but that continuations > are thus far unchanged from their old form. Is that correct? Yes. > I think this means that the idea of composable continuations, as > discussed in your blog, is still pending. Also, if I'm understanding > correctly, it seems to me that that idea is the same as having a 2 arg > call/cc. No we definitely have composable continuations. (Yay!) What we don't have are delimited non-composable continuations. >> Finally, delimiting call/cc makes it more expressive when invoked from a >> callback API. Imagine you have some inversion-of-control library that >> calls your code every so often -- currently in Guile you can't capture >> the continuation in one callback, then invoke it in the next, because it >> means your first callback would return twice. Well, truth is you can, >> but often callback-style APIs are implemented in C or such, and often >> such code can't handle multiple returns. > > Yes, I've hit this issue. It can be solved though, with Guile 1.8.x. This can be worked around in the inversion-of-control library via rewindable frames, or prohibited via with-continuation-barrier; but if the library is not continuation-safe, you can't invoke continuations captured in a callback outside of that callback, even in 1.8. Delimited non-composable continuations could solve this, though, because invoking a delimited non-composable continuation replaces the current continuation *only up to a prompt*, just as it was captured only up to a prompt. So you wrap each callback invocation in a prompt, and the Scheme programmer gets her call/cc, and the library implementor is left in peace. >> Relatedly, delimiting the continuations makes it easier to think about >> shipping them across the network. > > Interesting. It turns out we already have a serializer for most Guile objects, in the compiler. We'd just need to add cases for closures and continuations -- for which most of the work is already done. >> So, that's the upside. The downside of delimiting "full" continuations >> is that you can't capture the C stack. > > Why? Also I'm not sure what "full" means here. "Non-composable". The reason is that invoking a delimited non-composable continuation reinstates it relative to a prompt -- and the location of that prompt on the stack (both C and VM) is determined dynamically, so the internal stack pointers would need to be readjusted (frame pointers, etc). We can do that for the VM, because we know the stack format, but not for foreign code. >> Practically speaking... I think I can delimit call/cc with not much work >> (perhaps tomorrow). But it is a visible change (if you're looking), so I >> wanted to put this mail out there to get comments. > > Well, I hope that some of mine are useful. They were indeed, thanks for your comments. > I'm afraid I don't yet understand well enough (despite all of the > explanation above) what the impact of this visible change would be; > perhaps some more concrete examples would help. But it seems a very big > change, if it's only to produce delimited backtraces. Yes, perhaps it would be better to use prompts for start-stack, and then have make-stack search for prompts when narrowing its stack. But I do think we want non-composable continuations to be delimited. This will allow code entered at Guile's REPL, for example, to freely use call/cc, but not capture that part of the continuation that corresponds to the REPL itself -- because every expression entered there would be wrapped in a prompt. Anyway, thanks much for your comments. Let me know your reactions, and now on to Ludo's mail :-) Cheers, Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-04 19:54 call/cc and recursive vm invocations Andy Wingo 2010-03-06 17:13 ` Neil Jerram @ 2010-03-06 23:23 ` Ludovic Courtès 2010-03-07 13:39 ` Andy Wingo 1 sibling, 1 reply; 8+ messages in thread From: Ludovic Courtès @ 2010-03-06 23:23 UTC (permalink / raw) To: guile-devel Hi Andy! Andy Wingo <wingo@pobox.com> writes: > In case you haven't noticed yet, if you get an error at the REPL and ask > for a backtrace from the debugger, you get not only the frames that > pertain to your own computation, but also frames from the REPL > implementation itself. That's not good, and it's a regression. It’s a regression in ‘make-stack’, right? Can you remind me what caused it? > The solution is to implement start-stack for the VM. Prompts provide an > ideal set of primitives. So start-stack can be this: > > (define (start-stack tag exp) > (let ((prompt-tag (fresh-tag))) > (with-fluids ((stacks (acons tag prompt-tag (fluid-ref stacks)))) > (% prompt-tag > exp > (lambda (k . args) > (apply values k args)))))) > > This assumes some fluid is around named "stacks". Then we can implement > make-stack: > > (define (make-stack tag . narrowing) > (let ((prompt-tag (assq-ref (fluid-ref stacks) tag))) > (unless prompt-tag > (error "prompt tag not found quoi")) > (narrow-stack > (continuation->stack > (call/cc > (lambda (k) k) > prompt-tag)) > narrowing))) Looks nice! :-) > So, that's the upside. The downside of delimiting "full" continuations > is that you can't capture the C stack. [...] > But of course, to each implementation its own time, and this approach to > continuations has been nearing its expiration date for years now. Most C > code can't deal with nonlocal exits, let alone multiple returns. So the > utility of this construct has been limited. Indeed. In libguile, out of 40 ‘scm_dynwind_begin ()’ invocations, only 7 are ‘SCM_F_DYNWIND_REWINDABLE’ (and that doesn’t mean multiple returns would actually provide a sensible result in those cases.) [...] > Anyway, enough about that. Practically speaking, not capturing the C > stack means that you cannot invoke a continuation that was captured > outside the current VM invocation. IIUC, two things could happen (assuming ‘filter’ is a C function): 1. Failure at ‘call/cc’-time, because the stack contains multiple VM invocations intertwined with C function calls. For example: (filter (lambda (x) (call/cc ...)) lst) 2. Failure at the time the continuation is invoked, because it’s invoked in the context of a different VM invocation than ‘call/cc’. For example: (call/cc (lambda (k) (filter (lambda (x) (k ...)) lst))) You were referring to case #2. Is this correct? [...] > The most common causes for recursive incovations for a repl startup > were, in no order: > > primitive-load > load-compiled/vm > primitive-load-path > call-with-input-string (only once or so) > map > for-each > hash-for-each > scm_eval_closure_lookup (?) > scm_thunk_p (calls program's meta-thunk to get arity; wasteful GC-wise) > scm_resolve_module (calls Scheme resolve-module) > filter That’s for a REPL startup, but we have lots of primitives written in C. I’d expect a ‘call/cc’ failure to be likely in an arbitrary program. What do you think? > Practically speaking... I think I can delimit call/cc with not much work > (perhaps tomorrow). But it is a visible change (if you're looking), so I > wanted to put this mail out there to get comments. I had thought this > was a 2.2 feature, but given the make-stack implications, I'm thinking > it's a 1.9.9 feature. Reactions? I’d be rather inclined to wait until 2.2. While I agree that the usability of ‘call/cc’ is currently limited for the reasons you gave, I fear that doing away with the C stack capture may render ‘call/cc’ even less usable for code that exists, mixes C and Scheme, and has been able to work around the limitations. I also think that we should be stabilizing things if we want to release Real Soon Now, and that 2.2 doesn’t have to wait until 2020 anyway. :-) Another question is: can ‘make-stack’ be fixed in the current framework? This would be less elegant but also less disruptive. Cheers, Ludo’. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-06 23:23 ` Ludovic Courtès @ 2010-03-07 13:39 ` Andy Wingo 2010-03-07 16:55 ` Ludovic Courtès 0 siblings, 1 reply; 8+ messages in thread From: Andy Wingo @ 2010-03-07 13:39 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel Hi Ludo! On Sun 07 Mar 2010 00:23, ludo@gnu.org (Ludovic Courtès) writes: > Andy Wingo <wingo@pobox.com> writes: > >> In case you haven't noticed yet, if you get an error at the REPL and ask >> for a backtrace from the debugger, you get not only the frames that >> pertain to your own computation, but also frames from the REPL >> implementation itself. That's not good, and it's a regression. > > It’s a regression in ‘make-stack’, right? Can you remind me what caused > it? It's more that start-stack didn't work, see: d79d908ef0c421798b79bd72403b2a8fd196173c 373d251b4dd5153c6909898dc225d37d4948e3d6 107139eaadab946e9713748cdeacd07b22a181db >> Anyway, enough about that. Practically speaking, not capturing the C >> stack means that you cannot invoke a continuation that was captured >> outside the current VM invocation. > > IIUC, two things could happen (assuming ‘filter’ is a C function): > > 1. Failure at ‘call/cc’-time, because the stack contains multiple VM > invocations intertwined with C function calls. For example: > > (filter (lambda (x) > (call/cc ...)) > lst) I thought about making this fail, but it seemed a bit petty. Also it would preclude call/cc for the purpose of *inspecting* the continuation, as in the make-stack case. But of course this would work: (filter (lambda (x) (% suitable-prompt-tag (call/cc ...) ...)) lst) The mechanics of defining what prompt would delimit a one-arg call/cc are a little unspecified now; using the PLT idiom would fix that tho: (filter (lambda (x) (call-with-continuation-prompt (lambda () (call/cc ...)))) lst) > 2. Failure at the time the continuation is invoked, because it’s > invoked in the context of a different VM invocation than > ‘call/cc’. For example: > > (call/cc (lambda (k) > (filter (lambda (x) > (k ...)) > lst))) > > You were referring to case #2. Is this correct? No, actually this would usually work. The problem comes when the call/cc is not in the same VM invocation as the prompt; the location of the continuation invocation actually doesn't matter. To be clear: (call-with-continuation-prompt (lambda () (filter (lambda (x) (call/cc (lambda (k) (k x)))) lst))) I don't know how to make this work, because reinstating the continuation crosses foreign code (namely, filter). On the other hand I can imagine avoiding a longjmp() in this case, because the continuation is invoked within the same VM invocation as its capture, so we can avoid messing with the C stack at all; but this probably would not work: (call-with-continuation-prompt (lambda () (filter (lambda (x) (call/cc (lambda (k) (filter (lambda (x) (k x)) lst)))) lst))) Of course, assuming filter is implemented in C. >> The most common causes for recursive incovations for a repl startup >> were, in no order: >> >> primitive-load >> load-compiled/vm >> primitive-load-path >> call-with-input-string (only once or so) >> map >> for-each >> hash-for-each >> scm_eval_closure_lookup (?) >> scm_thunk_p (calls program's meta-thunk to get arity; wasteful GC-wise) >> scm_resolve_module (calls Scheme resolve-module) >> filter > > That’s for a REPL startup, but we have lots of primitives written in C. > I’d expect a ‘call/cc’ failure to be likely in an arbitrary program. > What do you think? Yeah it looks like in libguile there are about 200 instances of scm_call_*, but most of them probably aren't sensibly rewindable. Given that call/cc is not exposed to C programs, except via the now-internal scm_make_continuation, I don't expect breakage on the C front; and on the Scheme front, Guile 1.8 regularized a lot of that code by making scm_with_guile/scm_without_guile almost mandatory, and those create continuation barriers -- so I don't see this creating many problems from callback code. We're just left with people capturing continuations in filter functions &c, possible but not likely :) >> Practically speaking... I think I can delimit call/cc with not much work >> (perhaps tomorrow). But it is a visible change (if you're looking), so I >> wanted to put this mail out there to get comments. I had thought this >> was a 2.2 feature, but given the make-stack implications, I'm thinking >> it's a 1.9.9 feature. Reactions? > > I’d be rather inclined to wait until 2.2. While I agree that the > usability of ‘call/cc’ is currently limited for the reasons you gave, I > fear that doing away with the C stack capture may render ‘call/cc’ even > less usable for code that exists, mixes C and Scheme, and has been able > to work around the limitations. > > I also think that we should be stabilizing things if we want to release > Real Soon Now, and that 2.2 doesn’t have to wait until 2020 anyway. :-) Hm, OK :) I will finish my patch today then and push it to a branch. But let me know if you have a change of heart :) Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-07 13:39 ` Andy Wingo @ 2010-03-07 16:55 ` Ludovic Courtès 2010-03-07 19:34 ` Andy Wingo 0 siblings, 1 reply; 8+ messages in thread From: Ludovic Courtès @ 2010-03-07 16:55 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Happy septidi! Andy Wingo <wingo@pobox.com> writes: > On Sun 07 Mar 2010 00:23, ludo@gnu.org (Ludovic Courtès) writes: > >> Andy Wingo <wingo@pobox.com> writes: >> >>> In case you haven't noticed yet, if you get an error at the REPL and ask >>> for a backtrace from the debugger, you get not only the frames that >>> pertain to your own computation, but also frames from the REPL >>> implementation itself. That's not good, and it's a regression. >> >> It’s a regression in ‘make-stack’, right? Can you remind me what caused >> it? > > It's more that start-stack didn't work, see: > > d79d908ef0c421798b79bd72403b2a8fd196173c > 373d251b4dd5153c6909898dc225d37d4948e3d6 > 107139eaadab946e9713748cdeacd07b22a181db That was in Sept. 2008, but now ‘(make-stack #t)’, at least, works, right? (See, e.g., ‘stack->frames’ in ‘eval.test’.) What doesn’t work is ‘make-stack’ with an argument other than #t, right? > (filter (lambda (x) > (call-with-continuation-prompt (lambda () (call/cc ...)))) > lst) (For those wondering: <http://docs.plt-scheme.org/reference/cont.html#%28def._%28%28quote._~23~25kernel%29._call-with-continuation-prompt%29%29>.) >> 2. Failure at the time the continuation is invoked, because it’s >> invoked in the context of a different VM invocation than >> ‘call/cc’. For example: >> >> (call/cc (lambda (k) >> (filter (lambda (x) >> (k ...)) >> lst))) >> >> You were referring to case #2. Is this correct? > > No, actually this would usually work. The problem comes when the call/cc > is not in the same VM invocation as the prompt; Sure, but this example doesn’t use any prompt; IOW, the default prompt is at the beginning of the program. [...] >> That’s for a REPL startup, but we have lots of primitives written in C. >> I’d expect a ‘call/cc’ failure to be likely in an arbitrary program. >> What do you think? > > Yeah it looks like in libguile there are about 200 instances of > scm_call_*, but most of them probably aren't sensibly rewindable. > > Given that call/cc is not exposed to C programs, I’m also assuming that ‘call/cc’ is only ever used from Scheme. > except via the now-internal scm_make_continuation, I don't expect > breakage on the C front; The examples of possible breakage above are written entirely in Scheme. > and on the Scheme front, Guile 1.8 regularized a lot of that code by > making scm_with_guile/scm_without_guile almost mandatory, One is only supposed to leave guile mode when doing something that may block, like I/O. So you could have a C library that calls back to Scheme without ever leaving guile mode. > We're just left with people capturing continuations in filter functions > &c, possible but not likely :) I feel that this is more likely than you think. :-) There are all the cases inside libguile (we can eliminate some of them but perhaps not all, notably because it’d be a bit of work), and bindings to C library that callback to Scheme (Guile-Avahi, Guile-GNOME I guess, etc.). >>> Practically speaking... I think I can delimit call/cc with not much work >>> (perhaps tomorrow). But it is a visible change (if you're looking), so I >>> wanted to put this mail out there to get comments. I had thought this >>> was a 2.2 feature, but given the make-stack implications, I'm thinking >>> it's a 1.9.9 feature. Reactions? >> >> I’d be rather inclined to wait until 2.2. While I agree that the >> usability of ‘call/cc’ is currently limited for the reasons you gave, I >> fear that doing away with the C stack capture may render ‘call/cc’ even >> less usable for code that exists, mixes C and Scheme, and has been able >> to work around the limitations. >> >> I also think that we should be stabilizing things if we want to release >> Real Soon Now, and that 2.2 doesn’t have to wait until 2020 anyway. :-) > > Hm, OK :) I will finish my patch today then and push it to a branch. But > let me know if you have a change of heart :) Sure, thanks! :-) Ludo’. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-07 16:55 ` Ludovic Courtès @ 2010-03-07 19:34 ` Andy Wingo 2010-03-07 20:46 ` Ludovic Courtès 0 siblings, 1 reply; 8+ messages in thread From: Andy Wingo @ 2010-03-07 19:34 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel Greetings! On Sun 07 Mar 2010 17:55, ludo@gnu.org (Ludovic Courtès) writes: > Andy Wingo <wingo@pobox.com> writes: > >> On Sun 07 Mar 2010 00:23, ludo@gnu.org (Ludovic Courtès) writes: >> >>> Andy Wingo <wingo@pobox.com> writes: >>> >>>> In case you haven't noticed yet, if you get an error at the REPL and ask >>>> for a backtrace from the debugger, you get not only the frames that >>>> pertain to your own computation, but also frames from the REPL >>>> implementation itself. That's not good, and it's a regression. >>> >>> It’s a regression in ‘make-stack’, right? Can you remind me what caused >>> it? >> >> It's more that start-stack didn't work, see: >> >> d79d908ef0c421798b79bd72403b2a8fd196173c >> 373d251b4dd5153c6909898dc225d37d4948e3d6 >> 107139eaadab946e9713748cdeacd07b22a181db > > That was in Sept. 2008, but now ‘(make-stack #t)’, at least, works, > right? (See, e.g., ‘stack->frames’ in ‘eval.test’.) > > What doesn’t work is ‘make-stack’ with an argument other than #t, > right? That's correct, and the reason was that there were no delimiters produced by start-stack, because start-stack was stubbed out. >>> 2. Failure at the time the continuation is invoked, because it’s >>> invoked in the context of a different VM invocation than >>> ‘call/cc’. For example: >>> >>> (call/cc (lambda (k) >>> (filter (lambda (x) >>> (k ...)) >>> lst))) >>> >>> You were referring to case #2. Is this correct? >> >> No, actually this would usually work. The problem comes when the call/cc >> is not in the same VM invocation as the prompt; > > Sure, but this example doesn’t use any prompt; IOW, the default prompt > is at the beginning of the program. Point taken; I had assumed this expression was at the repl (which presumably would wrap each expression in a prompt). What I mean to say is that your case #2 will generally work but sometimes not. Incidentally, this unfortunate situation of "generally working but sometimes not" seems endemic to prompts, as they rely on a dynamically bound prompt, which might not be there, and whose presence cannot be statically checked for, AFAIK. >> and on the Scheme front, Guile 1.8 regularized a lot of that code by >> making scm_with_guile/scm_without_guile almost mandatory, > > One is only supposed to leave guile mode when doing something that may > block, like I/O. So you could have a C library that calls back to > Scheme without ever leaving guile mode. Of course. Of course it is also unlikely that said library could handle multiple returns, unless it were specifically designed for it. FWIW, Guile-GNOME almost always has to do the with/without dance. Ciao, Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: call/cc and recursive vm invocations 2010-03-07 19:34 ` Andy Wingo @ 2010-03-07 20:46 ` Ludovic Courtès 0 siblings, 0 replies; 8+ messages in thread From: Ludovic Courtès @ 2010-03-07 20:46 UTC (permalink / raw) To: guile-devel Hey! Andy Wingo <wingo@pobox.com> writes: > On Sun 07 Mar 2010 17:55, ludo@gnu.org (Ludovic Courtès) writes: > >> Andy Wingo <wingo@pobox.com> writes: >> >>> On Sun 07 Mar 2010 00:23, ludo@gnu.org (Ludovic Courtès) writes: >>> >>>> Andy Wingo <wingo@pobox.com> writes: >>>> >>>>> In case you haven't noticed yet, if you get an error at the REPL and ask >>>>> for a backtrace from the debugger, you get not only the frames that >>>>> pertain to your own computation, but also frames from the REPL >>>>> implementation itself. That's not good, and it's a regression. >>>> >>>> It’s a regression in ‘make-stack’, right? Can you remind me what caused >>>> it? >>> >>> It's more that start-stack didn't work, see: >>> >>> d79d908ef0c421798b79bd72403b2a8fd196173c >>> 373d251b4dd5153c6909898dc225d37d4948e3d6 >>> 107139eaadab946e9713748cdeacd07b22a181db >> >> That was in Sept. 2008, but now ‘(make-stack #t)’, at least, works, >> right? (See, e.g., ‘stack->frames’ in ‘eval.test’.) >> >> What doesn’t work is ‘make-stack’ with an argument other than #t, >> right? > > That's correct, and the reason was that there were no delimiters > produced by start-stack, because start-stack was stubbed out. OK, got it. >>> and on the Scheme front, Guile 1.8 regularized a lot of that code by >>> making scm_with_guile/scm_without_guile almost mandatory, >> >> One is only supposed to leave guile mode when doing something that may >> block, like I/O. So you could have a C library that calls back to >> Scheme without ever leaving guile mode. > > Of course. Of course it is also unlikely that said library could handle > multiple returns, unless it were specifically designed for it. Yes, right. Thanks for taking the time to explain! Ludo’. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2010-03-07 20:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-03-04 19:54 call/cc and recursive vm invocations Andy Wingo 2010-03-06 17:13 ` Neil Jerram 2010-03-07 13:13 ` Andy Wingo 2010-03-06 23:23 ` Ludovic Courtès 2010-03-07 13:39 ` Andy Wingo 2010-03-07 16:55 ` Ludovic Courtès 2010-03-07 19:34 ` Andy Wingo 2010-03-07 20:46 ` Ludovic Courtès
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).