From leavens@judy.cs.iastate.edu. Tue Feb 12 17:13:00 1991
Date: Tue, 12 Feb 91 17:07:35 CST
From: leavens@judy.cs.iastate.edu. (Gary Leavens)
To: leavens@judy.cs.iastate.edu
Subject: c closures

Dennis Ritchie writes:

> I've been hearing some fuss about "lambda" and "fn" and I just don't
> get it.  What's the big deal?  I can do all this in my favorite
> langauge: C.  For example, consider everybody's favorite
> example of a curried function in SML, which is the following
> curried version of +.
> 
> 	(* curried addition in (desugared) SML *)
> 	val cadd = (fn x => (fn y => x + y));
> 
> For example, ((cadd 2) 3) returns 5.
> 
> But in C, functions are objects too, and I can pass around function
> pointers.  So consider the following program to do curried addition.
> 
> 	/* curried addition program in ANSI C */
> 	#include <stdio.h>
> 
> 	typedef int (*func)(int);  /* functions that take and return ints */
> 
> 	int takes_y(int y) { return(x + y); }
> 
> 	func cadd(int x) { return(&takes_y); }
> 
> 	int main() { printf("%i\n", (cadd(2))(3)); }
> 
> I just about have this working, and it seems like it does the
> same thing as the SML code.  (Only problem is that the variable
> "x" used in "takes_y" is undefined, but I'll fix that soon.)
> Doesn't this prove that SML, and "fn" are no big deal?
> Who cares anyway?
> 
> 	Dennis Ritchie

Dennis and his problem with the variable "x" point out the value
of SML's closures.  The thing is that (cadd(2)) in SML
is a closure, and so automatically keeps track of the binding
of x to 2.  If we work out Dennis's problem with "x" we can see
even more of the value of closures in SML.  Consider the
following:

   /* corrected curried addition program in ANSI C */
   #include <stdio.h>
   
   typedef int (*func)(int, int);   /* binary functions on integers */
   typedef struct { func f; int x; } closure;     /* closure records */
   typedef closure *closurePtr;        /* pointrs to closure records */
   
   int add(int x, int y) {return x + y; }
   
   closurePtr cadd(int x) {
     closurePtr c;
     c = (closurePtr) malloc(sizeof(closure));   /* no check for NULL */
     c->f = add;
     c->x = x;
     return c;
   }
   
   int invoke_closure(closurePtr c, int arg) { return (c->f)(c->x, arg); }
   
   int main() { printf("%i\n", invoke_closure(cadd(2), 3)); }


I changed Ritchie's "takes_y" procedure into "add",
but the most significant change is to have "cadd" return
a pointer to a closure record, which is (as in SML) a structure containing
both a function (add) and a binding for x.  In this way
one can have multiple closures returned by "cadd" lying around.

If one made "x" a global variable in Ritchie's program,
then there would be a problem in the following:
  func add2, add3;
  add2 = cadd(2);
  add3 = cadd(3);
  printf("%i\n", add2(27));
which will print 30, not 29.

To summarize, fn builds closures in SML.
And a closure is a function together with the environment in which it
was created.

	Gary
