-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 12-Nov-06, at 9:07 AM, Christian wrote:
Hello
apply is quite a handy simple mechanism for destructuring lists, but: if the list doesn't match the function prototype, an exception is thrown. Sometimes I don't want to get an exception but instead try to apply another function (more generally: to back-track).
So I'm looking for an alternative apply function which takes an alternative continuation as a third argument (instead of the implicit consing mechanism offered by standard apply when giving it multiple arguments):
(cond-apply fn1 lis (lambda () (cond-apply fn2 lis (lambda () (error "no match:" lis)))))
or
(or (cond-apply fn1 lis) (cond-apply fn2 lis) (error "no match:" lis))
call-with-exception-* don't do any good since it's relatively slow and, worse, would leave the handler in place while the called function is running.
I'm remembering a lowlevel function to get the arity of a function (returning a positive fixnum for a fixed number of arguments, and a negative fixnum if the function is taking a rest argument). Strangely, I cannot find it again now. What is it called? (Or did I see it in Chicken, not Gambit?) If that function is efficient, I could write cond-apply in a fairly efficient way.
The concept of "the arity of a procedure" is ill-defined in general when you have optional parameters. The mechanism you mention exists at compile-time for macros, but that is of no use at run time.
The simplest thing to do is to use call/cc and exceptions, but as you mention that is moderately expensive and has scoping problems.
What you really want is for cond-apply to save the error-continuation in the continuation (on the stack), which is really inexpensive, and to access the error-continuation only when there is a wrong-number-of- arguments exception. This can be done with this code, which should be compiled:
(declare (extended-bindings) (block) (not inline))
(define (cond-apply proc args $wrong-nb-args)
(declare (not interrupts-enabled) (environment-map))
(let ((results (##apply proc args))) (##first-argument $wrong-nb-args) results))
(define (##raise-wrong-number-of-arguments-exception-nary proc . args)
(declare (not interrupts-enabled))
(wrong-number-of-arguments proc args))
(define (##raise-wrong-number-of-arguments-exception proc args)
(declare (not interrupts-enabled))
(wrong-number-of-arguments proc args))
(define (wrong-number-of-arguments proc args)
(define (err) (error "wrong number of arguments" proc args))
(##continuation-capture (lambda (cont) (if (##eq? cond-apply (##subprocedure-parent (##continuation-ret cont))) (let ((binding (##continuation-locals cont '$wrong-nb-args))) (if (##pair? binding) (let (($wrong-nb-args (##cdr binding))) ($wrong-nb-args)) (err))) (err)))))
(pp (cond-apply cons '(1 2 3) (lambda () (cond-apply sin '(1 2 3) (lambda () 999)))))
All of this works because the procedures ##raise-wrong-number-of- arguments-exception and ##raise-wrong-number-of-arguments-exception- nary are tail-called by ##apply (i.e. continuation = inside the cond- apply) so we can simply check the continuation frame at the top of the stack to find the value of $wrong-nb-args. The (declare (environment-map)) is necessary to keep the name of the variables in the continuation frame so that ##continuation-locals can find it.
Marc