[comp.lang.c] anonymous functions

chris@mimsy.UUCP (Chris Torek) (05/07/88)

>In article <11325@mimsy.UUCP> I mentioned
>>(Of course, if you add unnamed aggregates, you should also add
>>unnamed functions.)

In article <282@teletron.UUCP> andrew@teletron.UUCP (Andrew Scott) asks:
>... How would unnamed functions be implemented?  How would they be used?

The implementation is obvious (he said smugly).  You could even do it
in a PCC-style compiler:

	/* source */
	void bar(int (*fp)());	/* bar takes one argument */

	void
	foo() {
		bar(	/* call bar with the pointer from...: */
			/* (here comes the anonymous function def.) */
			int (){ int i; i = 3; return (i); }
		   );
	}

	/* sample dumb compiler output */
	_foo:	.globl	_foo
		sub	$L1,sp		# create local frame space
		jbr	L2		# branch around the anonymous fn.
	L3:
		sub	$L4,sp		# create local frame space
		mov	$3,-4(fp)	# i=3
		mov	-4(fp),r0	# return (i)
		ret
		.set	L4,4		# anonymous function (L3) needs
					# 4 bytes of stack
	L2:	push	$L3		# push address of anonymous fn.
		call	_bar		# bar(...)
		pop	r0		# clean up
		ret			# end of foo()
		.set	L1,0		# function foo needs 0 bytes

As for uses, anonymous functions are much like anonymous aggregates:
you use them to pass to other functions, or to set local variables
(in C, pointers to functions).

	void
	foo() {
		void (*fp)() = void () { code; }
		...
		(*fp)();
	}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

root@mfci.UUCP (SuperUser) (05/10/88)

Expires:

Followup-To:

Distribution:

Keywords:


In article <11385@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>In article <282@teletron.UUCP> andrew@teletron.UUCP (Andrew Scott) asks:
>>... How would unnamed functions be implemented?  How would they be used?
>
>The implementation is obvious (he said smugly).  You could even do it
>in a PCC-style compiler:
>
>	/* source */
>	void bar(int (*fp)());	/* bar takes one argument */
>
>	void
>	foo() {
>		bar(	/* call bar with the pointer from...: */
>			/* (here comes the anonymous function def.) */
>			int (){ int i; i = 3; return (i); }
>		   );
>	}

Of course, this opens a whole can of worms about how to handle up-level
references to automatics, etc.  For example, what does the following do?

    int     x = 123;

    main()
    {
        int     x = 456;
        void  (*fp)() = void () { printf("%d\n", x); };

        (*fp)();
    }

You could make the reference to x illegal, but it wouldn't last.  Some
implementations would give you the external x, so you'd get 123, and
others would give you the local x, so you'd get 456.  The latter would
be more generally powerful and useful, but then you'd have to have displays,
etc.  You'd also be put in the position of justifying why named nested
functions aren't supported, and you'd end up having to support those as
well...

>As for uses, anonymous functions are much like anonymous aggregates:
>you use them to pass to other functions, or to set local variables
>(in C, pointers to functions).
>
>	void
>	foo() {
>		void (*fp)() = void () { code; }
>		...
>		(*fp)();
>	}

Don't you mean:

	void
	foo() {
		void (*fp)() = void () { code };
		...
		(*fp)();
	}

chris@mimsy.UUCP (Chris Torek) (05/11/88)

In article <393@m3.mfci.UUCP> root@mfci.UUCP (SuperUser) writes:
>Of course, this opens a whole can of worms about how to handle up-level
>references to automatics, etc.

Just make it illegal.  If you want access to some `parent's' variables
(in quotes because to a large extent, the scoping is illusory), you must
pass them to the anonymous function:

>    int x = 123;
>    main() {
>        int x = 456;
>        void (*fp)() = void () { printf("%d\n", x); };
>        (*fp)();
>    }

becomes

	int x = 123;
	main() {
		int x = 456;
		void (*fp)(int) = void (int x) { printf("%d\n", x); };
		(*fp)(x);
	}

>You could make the reference to x illegal, but it wouldn't last.
>[stuff about displays deleted]

I admit it `tastes odd', yet I am not sure it would not last.  I
would much rather not require static links or displays; I feel that
these are often just excess baggage: that they are not worth their
cost.  In practise, when I need access to uplevel variables, I do
it through what I call contexts.  These are rather similar to the
class pointers C++ passes to class functions, which are accessed
via the keyword `this'.  In C, there is no keyword: you have to do
it yourself:

	struct hitcontext {
		char	*str;
		int	hits;
	};
	static void hits_helper(void *context, struct tentry *te) {
		struct hitcontext *f = context;
		if (strstr(te->te_name, f->str) != NULL)
			f->hits++;
	}
	int hits(char *str, struct table *t) {
		struct hitcontext hc;
		hc.str = str;
		hc.hits = 0;
		table_iterate(t, hits_helper, (void *)&hc);
		return (hc.hits);
	}

(This is a simplified version of some real code.)  In this case,
it would be nice to keep both the hitcontext structure declaration
and the hits_helper function definition local to function hits.
Not necessary---just as unnamed aggregates are not necessary---but
convenient.  Can you imagine C without unnamed char arrays?

	int main() {
		static char S1[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 
				    'o', 'r', 'l', 'd', '\n', 0 };
		static char S2[] = {'i', ' ', '=', ' ', '%', 'd', '\n', 0 };
		int i;

		printf(S1);
		...
		printf(S2, i);
		return (0);
	}

Yuck!

Admittedly, anonymous functions are less useful in C than anonymous
strings.

[me:]
>>	foo() {
>>		void (*fp)() = void () { code; }

>Don't you mean:
>	foo() {
>		void (*fp)() = void () { code };

Yes.  (Just a typographic error....)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

throopw@xyzzy.UUCP (Wayne A. Throop) (05/11/88)

> chris@mimsy.UUCP (Chris Torek)

Once again, Chris writes the exact article I had in mind, only better
than I might have done it.  I have only one nit to pick.  In telling 
"what good are they" of anonymous functions, Chris says:

> As for uses, anonymous functions are much like anonymous aggregates:
> you use them to pass to other functions, or to set local variables
> (in C, pointers to functions).

... which is exactly correct, but he omits giving a convincing example,
like to supply a signal handler routine that just sets an external bit,
or a compare or swap routine for a qsort, or a hash routine for a table
manipulation routine, or whatever.  The point is, why name this little
bit of code that is required to be a function because of the way the
library is organized?  When extremely short as in these examples, giving
it a name is silly and wastefull.

Example invocations:

        signal( SIGINT, void (int sig){ interrupted = 1; } );

        qsort( (void *)tbl, n_elt, n_elt*sizeof(tbl_elt_t),
               int (void *a, void *b){
                   return( strcmp( (tbl_elt_t *)a->name, 
                                   (tbl_elt_t *)b->name ) ); } );

	/* crummy hash function, but good enough for whatever purpose: */
        hfind( name, table, int (char *s){ return(*s); } );

--
He wonders if he too might have made a similar mistake.
        --- "Seen and Not Seen" by Talking Heads from "Remain in Light"
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

peter@ficc.UUCP (Peter da Silva) (05/18/88)

Reposted because news wasn't getting off-site. My fault.

In article <282@teletron.UUCP>, andrew@teletron.UUCP (Andrew Scott) writes:
> The { .. } syntax for aggregate declar-
> ations seems natural enough to use for unnamed aggregates, but how would it
> be done for functions?

(lambda nil (...

Just kidding. How about:

	a = (int ()(int i)) {
		for(i=0; i < 10; i++)
			if(foo(i)==MAGIC) break;
		return i;
	}(b);

And again:

	int (*f)(int) = &(int ()(int i)) { ... };

	a = (*f)(b);

Yow! Auto functions! Back to BCPL. But I think it's neat.

In article <2706@geac.UUCP>, daveb@geac.UUCP (David Collier-Brown) writes:
> | In article <11325@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
>              qsort(base, nel, width, compar)
>              char *base;
>              int (*compar)();

> 	EXAMPLE
>              qsort(base, nel, width, { return zcomp($1,$2) });

	qsort(base, nel, width, &(int ()(int a, b)){ return zcomp(a, b); } );

> 	Note the problem with parameters...

I'm actually beginning to like this syntax. Is this a fatal character flaw?
-- 
-- Peter da Silva, Ferranti International Controls Corporation.
-- Phone: 713-274-5180. Remote UUCP: uunet!nuchat!sugar!peter.

peter@ficc.UUCP (Peter da Silva) (05/19/88)

In article <862@xyzzy.UUCP>, throopw@xyzzy.UUCP writes:
>         signal( SIGINT, void (int sig){ interrupted = 1; } );

>         qsort( (void *)tbl, n_elt, n_elt*sizeof(tbl_elt_t),
>                int (void *a, void *b){
>                    return( strcmp( (tbl_elt_t *)a->name, 
>                                    (tbl_elt_t *)b->name ) ); } );

> 	/* crummy hash function, but good enough for whatever purpose: */
>         hfind( name, table, int (char *s){ return(*s); } );

I think that the syntax is a bit wedged here. What you're actually doing
is akin to typecasting:

	(void (*)(int sig)){ interrupted = 1; }
	(int (*)(void *a, void *b)) { ... }
	(int (*)(char *s)){ return s; }

A bit more verbose, but should require less patching of the language
definition.
-- 
-- Peter da Silva, Ferranti International Controls Corporation.
-- Phone: 713-274-5180. Remote UUCP: uunet!nuchat!sugar!peter.

peter@ficc.UUCP (Peter da Silva) (05/19/88)

In article <862@xyzzy.UUCP>, throopw@xyzzy.UUCP writes:
>         signal( SIGINT, void (int sig){ interrupted = 1; } );

Of course if you went with this syntax instead of the typecast syntax, there's
no good reason to disallow:

	signal ( SIGINT, void (sig) int sig; { interrupted = 1; } );

After all, pre-ANSI declarations are still allowed.

I'd hate to have to write the compiler, though.
-- 
-- Peter da Silva, Ferranti International Controls Corporation.
-- Phone: 713-274-5180. Remote UUCP: uunet!nuchat!sugar!peter.

mcgrath@tully.Berkeley.EDU.berkeley.edu (Roland McGrath) (05/21/88)

) I'm actually beginning to like this syntax. Is this a fatal character flaw?

Oh, probably.  But I've got many fatal flaws, they tell me, and you and I
seem to be alive.  Ah, but is your character alive?  Well, perhaps not,
but then you're unflawed, so rejoice!

karl@haddock.ISC.COM (Karl Heuer) (05/24/88)

In article <799@.UUCP> peter@ficc.UUCP (Peter da Silva) writes:
>In article <862@xyzzy.UUCP>, throopw@xyzzy.UUCP writes:
>>         signal( SIGINT, void (int sig){ interrupted = 1; } );
>Of course if you went with this syntax instead of the typecast syntax, there's
>no good reason to disallow:
>	signal ( SIGINT, void (sig) int sig; { interrupted = 1; } );
>After all, pre-ANSI declarations are still allowed.

But they are obsolescent.  They exist only for backward compatibility, which
is not an issue when you're adding something new like anonymous functions.

I'm not sure how easy it is to parse, anyway; I think I'd be satisfied with my
earlier proposal:
	signal(SIGINT, remote(void (int sig), { interrupted = 1; }));
	ctime(&remote(long, 0));

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint