flow of control issues Tom Lord (26 Dec 2003 18:53 UTC)
|
Re: flow of control issues
Michael Sperber
(27 Dec 2003 16:21 UTC)
|
flow of control issues Tom Lord 26 Dec 2003 19:17 UTC
The draft quite sanely protects C code from always having to cope with multiple returns (not to mention state-saving) as via upward continuations. C code should not, however, be _prevented_ from constructing an upwards continuation. The draft is silent on tail call optimization which is an unfortuante omission that limits, for example, the kinds of higher-order procedures that can be implemented using the FFI. It's silent about asynchronous interrupts which, while not standard Scheme, are likely to be an important feature of most implementations. In an interactive application especially, absense of support for asynchronous interruption in "built-in" procedures can spoil an application's usability. It uses non-local exits for errors which raises the unwind-protection issues I mentioned earlier. I would therefore like to suggest the draft be revised on four points: 1) Don't use non-local exits for errors. Instead, provide a mechanism for returning error codes. If Scheme return values are to be passed via output parameters, then there is a convenient interface for this: error_code = SCHEME_CAR (&return_value, instance, &pair); if (error_code) { /* clean up and return an error to our caller */ return error_code; } /* otherwise, continue normally. The call to CAR * succeeded. */ In addition to avoiding non-local exits in the FFI, this style of error handling is closer to what is customary in C. 2) Do provide polling for asynchronous interruption. As in: while (very_long_loop) { ... if (SCHEME_INTERRUPT_POLL (instance)) { /* cleanup and return early */ return SCHEME_ERR_INTERRUPT; } ... } 3) Do provide a "trampoline-on-the-ceiling" style of tail calls for C. As in: scheme_error_code fn ( [...] ) { [...]; /* instead of returning a value, something vaguley like: */ SCHEME_CONSTRUCT_APPLICATION (&result, instance, &proc, &arg1, &arg2, ...); return SCHEME_ERR_MAKE_TAILCALL; } in which case my caller is responsible for performing the procedure application I constructed. While certainly not as fast as what an implementation can do internally, such tail calls are at least safe-for-space and portable. The converse of that is that the FFI needs to specify how to call a function which might request the caller to complete a tail call. 4) Do provide upward continuations from C. I don't have an interface sketch but, briefly, while the current interfaces for call-outs are fine, it should also be possible to write a C primitive that accepts as input a first-class representation of its continuation and returns the same. Used in a manner similar to the mechanism shown above for tail calls, this can permit FFI-using primitives whose state can be captured in an upward continuation. -t