<brlewis@my-deja.com> wrote:
+---------------
| I associate Scheme with the use of procedures as data (a.k.a. higher-order
| functions). I programmed in C for 12 years and rarely used procedures
| as data. The functionality is there, but not nearly as convenient as
| with Scheme.
|
| If I were to go back to C now, I imagine I'd use procedures as data a
| lot more, now that Scheme has let me explore the benefits.
+---------------
I've always heavily used C's "procedure pointers" as data, but it wasn't
until I had been hacking Scheme for a while that I found myself routinely
using *closures* when writing in C. It's a bit awkward at first, especially
since there's no natural way to write "apply" or "funcall" for variadic
args other than with a massive "switch(nargs)", but if you restrict yourself
to vararg-able arglists, it *can* be done.
Similarly, "lambda" per se isn't quite doable, but you can imagine a sort
of "make_closure" that can work well enough. E.g., in one place you might
say something like this:
typedef struct closure {
closure_proc func();
closure_data data[1]; /* usual hack for malloc'd variadics */
} closure;
...
closure *foo;
extern closure_proc bar();
...
foo = make_closure(bar, arg1, arg2, ... NULL);
...
where "make_closure" allocates a block of memory and saves its args in it,
and in another place use it as:
result = apply_closure(foo, other, args, here, NULL);
where "apply_closure" [*very* rough sketch only!!] might be:
void *
apply_closure(closure *clo, ...) {
int argc;
void **argv;
...something here to scan the vararg list
and set "nargs" & "argv", then...
switch (argc) {
case 0: return (clo->func)(clo);
case 1: return (clo->func)(clo, argv[0]);
case 2: return (clo->func)(clo, argv[0], argv[1]);
case 3: return (clo->func)(clo, argv[0], argv[1], argv[2]));
...
}
}
The assumption is, of course, that "bar" knows to look in its first arg
for its closure environment, which it must know how to deconstruct.
All of this gets a *lot* easier, of course, if the procedure call
protocol you're using is some variant of the "argc/argv" style. Many of
the "extension language" frameworks, such as Tcl & MzScheme (to name but
two that use this style), allow you to provide a closure-equivalent when
registering a procedure with the system. For example, in MzScheme if you
register a C procedure with "scheme_make_closed_prim_w_arity()", one of the
args is an opaque cookie that MzScheme will pass back when your procedure
is called from Scheme. Likewise, in Tcl the call "Tcl_CreateCommand()"
takes a "ClientData" cookie as one arg, which is passed back when Tcl
calls your C routine.
-Rob
-----
Rob Warnock, 31-2-510 rpw3@sgi.com
SGI Network Engineering <URL:http://reality.sgi.com/rpw3/>
1600 Amphitheatre Pkwy. Phone: 650-933-1673
Mountain View, CA 94043 PP-ASEL-IA