Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!crackers!jjmhome!smds!sw From: sw@smds.UUCP (Stephen E. Witham) Newsgroups: comp.lang.misc Subject: Re: On whether C has first-class composable functions Summary: Examples of anonymous, composable C "functions" Message-ID: <289@smds.UUCP> Date: 8 Jan 91 22:56:47 GMT References: <1990Dec29.110202.3862@mathrt0.math.chalmers.se> <1991Jan5.182428.615@mathrt0.math.chalmers.se> Organization: SMDS Inc., Concord, MA Lines: 177 To me, the "power" of a language has to do with what concepts you can express DIRECTLY. Or, it's the inverse of the amount of twisting you have to do to express something you are conceiving of in a certain way. Having to include a compiler or interpreter for another language is "too" indirect. The syntax and symantics have to support what you're doing...well, "directly". But I'm not too strict about what I mean by that. For example, here's a way to compose functions that I think is not too twisted, either syntactically or in terms of the overhead of things I have to define in the beginning. What do you all think? With this method, functions are annonymous, but function TYPES are not. "Types" meaning, things you have to define with C code. I think even that restriction could be overcome with macros in a way that somebody in this thread (who? sorry.) described earlier, but that might involve more twisting than it's worth. Here's the simplest version: ---------------------------- Code version 1 follows ------------------------ /* compose.c -- experiment in "second-class, composable, anonymous functions" Steve Witham Jan. 8, 1991 */ #include void *malloc(); /* Basic definitions for "functions" */ typedef struct { int (*c)(); /* code */ void *d; /* data */ } Func; #define APPLY(f,arg) ((f)->c)( (f)->d, arg ) int apply( f, arg ) /* Use apply, not APPLY, if f or arg is non-trivial. */ Func *f; int arg; { return APPLY( f, arg ); } /* --- A --- */ /* Definitions for "adder" function type... */ int adderBody ( d, arg ) int *d; int arg; { return *d + arg; } Func * adder ( i ) /* Create an Func that adds a constant to its argument. */ int i; { Func *result; result = (Func *) malloc( sizeof( Func ) ); result->c = adderBody; result->d = (void *) malloc( sizeof( int ) ); *( (int *) result->d ) = i; return result; } /* Definitions for composing functions... */ typedef struct { Func *f, *g; } FuncPair; int composeBody ( d, arg ) FuncPair *d; int arg; { APPLY( d->f, APPLY( d->g, arg ) ); } Func * compose ( f, g ) Func *f, *g; { Func *result; result = (Func *) malloc( sizeof( Func ) ); result->c = composeBody; result->d = (void *) malloc( sizeof( FuncPair ) ); ((FuncPair *) result->d) ->f = f; ((FuncPair *) result->d) ->g = g; return result; } /* --- B --- */ /* Here's a macro to print the text of an expression with its value... */ #define ANNOUNCE(expr) printf( "expr = %d\n", expr ); /* And finally... */ main ( ) { ANNOUNCE( apply( adder(1), 2 ) ); ANNOUNCE( apply( adder(3), apply( adder(4), 5 ) ) ); ANNOUNCE( apply( compose( adder(5), adder(6) ), 7 ) ); } ------------------------- End of version 1 ------------------------ Here is a variation to ponder. Replace the section between /* --- A --- */ and /* --- B --- */ with the following: -------------------------- Start of replacement code -------------------- /* Now a macro for defining new function-creating functions... */ #define FUNCMAKER(funcmaker,argdecls,setupcode,bodyname,bodycode) \ int bodyname( d, arg )\ struct{argdecls} *d; \ int arg; \ { bodycode; } \ Func *funcmaker \ argdecls \ { \ Func *result; \ struct{argdecls} *d; \ \ result = (Func *) malloc( sizeof( Func ) ); \ result->c = bodyname; \ *((void **) &d) = malloc( sizeof( struct{argdecls} ) ); \ result->d = (void *) d; \ setupcode; \ return result; \ } /* Definitions for "adder" function type... */ FUNCMAKER(adder(i), int i;, d->i = i, adderBody, return d->i + arg) /* Definitions for composing functions... */ FUNCMAKER(compose(f,g), Func *f; Func *g;, { d->f = f; d->g = g; }, composeBody, return apply( d->f, apply( d->g, arg ) ) ) ------------------------- End of replacement --------------------- This is totally unpolished, but both versions above work on my machine. The question I'm interested in is, given that you hide the right stuff in include files and subroutine libraries, how TWISTED is this method to use in the places where you'd want "first class, composable, anonymous functions?" This is actually something I want to do sometimes (for instance, passing an option to a compare function that I'm passing to a sort routine), so I'm really interested in y'all's oppinions. --Steve Witham, The Squeaky Wheel Not-the-fault-of: SMDS, Inc. If-All-Else-Fails: (508)369-7398