[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

flow of control issues



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