[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: good books on scheme?



Lo, on Wednesday, February 7, Immanuel Litzroth did write:

> >Let me point out though that using the terminoloy 'closure' makes sense
> >when you discuss first-class procedures with an uneducated C hacker who
> >thinks that pointers to functions are first-class procedures. Decades
> >after "LISPers" understood this point, C hackers still don't see the
> >difference.

> Not being a C hacker, but definitely uneducated I wonder if the greatest 
> difference between C function pointers and first-class procedures is not the 
> fact that they cannot be created at runtime because you have neither the 
> compiler nor the symboltable present?

Creating a new closure at runtime doesn't actually require the compiler.
You can emit the object code for the closure at compile time, then bind
that object code with an environment at run-time.  (I suspect this may get
much more complex in the face of ML-like polymorphic types, but that is
more than I know.)

> Aren't C functions closures in the sense mr Hailperin described?

Not in the sense in which I understand the word `closure', no.

To take a fairly silly example in Scheme:

(define make-adder
  (lambda (x)
    (lambda (y)
      (+ x y))))

The result of applying make-adder to an argument is a procedure which
remembers the value of x at the time when the procedure was created (i.e.,
the value originally supplied as the argument to make-adder).  In other
words, it's a procedure closed over x.

Now, you can't do this in C, at least not directly.  First, in standard C,
you can't nest function declarations.  However, some compilers (like
earlier versions of gcc; don't know if they still do this) did allow you do
it, but with one big caveat:

typedef int (*int2intfunc)(int);    // pointer to int->int---*ob*viously!

int2intfunc make_adder(int x)
{
    int adder_result(int y)
    {
        return x + y;
    }

    return &adder_result;
}

In C, x's lifetime ends when make_adder returns.  So, even if you've
managed to get a pointer to adder_result to last beyond make_adder's
return, invoking it will produce undefined results, because x's value is
now garbage.  (In implementation terms, adder_result usually refers to
make_adder's stack frame, which no longer exists after make_adder returns.)

You *can* do closures in C++, although it's not as convenient as in Scheme:

class Adder
{
private:
    int a;
public:
    Adder(int _a) : a(_a) { }

    int add(int b) { return a + y; }

    // or, if you don't mind the ugly notation,
    int operator()(int b) { return a + b; }
};

Adder make_adder(int x)
{
    return Adder(x);
}

The Adder object, which represents a closure, carries not only the
operation (the body of the add method) but also the value of x, so invoking
its add method is well defined.

This is the best, if verbose, way I've found to do callbacks, for
instance.

(Of course, since you can do object-oriented programming in C, you can also
use this trick in C as well.  You just have to write all of the object
infrastructure, like virtual method dispatch, yourself.)

Richard