Subject: Re: Uses of LISP
From: rpw3@rigden.engr.sgi.com (Rob Warnock)
Date: 27 Mar 2001 03:44:31 GMT
Newsgroups: comp.lang.scheme
Message-ID: <99p2av$4bn09$1@fido.engr.sgi.com>
<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