[comp.object] Closures in C++

gyro@kestrel.edu (Scott Layson) (03/24/91)

I seem to have missed most of the discussion on closures, alas.  Since
terminology has been an issue, let me briefly summarize the way I am
used to using the words (these are informal definitions, but should
convey the idea):

 -- A "closure", to me, is a firstclass procedure object which,
    loosely speaking, carries its own context with it.

 -- A "context" is a mapping from variables to values.

 -- An "instance" is a mapping from operation names to closures, such
    that all of these closures share a single context.

Therefore an instance, to me, is not exactly the same thing as a
closure, but they are very closely related: a closure can do only one
thing, while an instance can do any of several things depending on
which operation is invoked on it.

My question is simply this: has there been any discussion on this
newsgroup (or anywhere else you are aware of) of the possibility of
introducing closures, in this sense, into C++, by decreeing that a
pointer to function (horrid bit of terminology, that) shall be
implemented as a pair of a code pointer and a context pointer?  (Not
to say that that's the only possible implementation, but I think it's
a quite sensible one.)

Just so people have some idea what I'm talking about, a brief example:

class C {
     // constructors etc. omitted
     int (*f1())();    // member function returning (int (*)())
                       // (i.e. pointer to function of no args
                       // returning int)
  private:
     int f2();
     // other member variables omitted
};

int (*C::f1())()
{
     return f2;        // alternate notation for `&f2'
}


The idea here is that the thing f1 returns would be like a pointer to
function, except that it would perform the f2 operation *on the object
on which f1 had been invoked*.

-- Scott Layson Burson

jgk@osc.COM (Joe Keane) (03/25/91)

In article <1991Mar24.020701.27641@kestrel.edu> gyro@kestrel.edu (Scott
Layson) writes:
>Therefore an instance, to me, is not exactly the same thing as a
>closure, but they are very closely related: a closure can do only one
>thing, while an instance can do any of several things depending on
>which operation is invoked on it.

But when someone says `an object is a closure', it's understood that the
operation to be performed on the object is the first argument to the closure.
So there's really no difference at all if you take this view.

For example, you can define cons cells in terms of lambda calculus.  You can
define cons as `lambda(x,y).lambda(f).f(x,y)', car as `lambda(x,y).x', and cdr
as `lamdba(x,y).y'.  Actually, this works out to be very close to standard
definition if you consider that a closure is a bunch of arguments allocated on
the heap.  It looks much different at first, but really it's just a different
way of interpreting the same thing.
--
Joe Keane, object hacker

gyro@kestrel.edu (Scott Layson) (03/26/91)

In article <4687@osc.COM> jgk@osc.COM (Joe Keane) writes:
>In article <1991Mar24.020701.27641@kestrel.edu> gyro@kestrel.edu (Scott
>Layson) writes:
>>Therefore an instance, to me, is not exactly the same thing as a
>>closure, but they are very closely related: a closure can do only one
>>thing, while an instance can do any of several things depending on
>>which operation is invoked on it.
>
>But when someone says `an object is a closure', it's understood that the
>operation to be performed on the object is the first argument to the closure.
>So there's really no difference at all if you take this view.

That's true.  But I don't take that view.  I prefer to think of
operation dispatch as happening outside the object rather than inside
it.

Why?  Only because some, though certainly not all, object-oriented
languages take the same view (C++, for instance [sorry]).

I will grant that this is solely a matter of convention, but it's a
convention that is often useful and therefore I adhere to it.

-- Scott Layson Burson
Gyro@Reasoning.COM

jgk@osc.COM (Joe Keane) (03/27/91)

In article <4687@osc.COM> i write:
>But when someone says `an object is a closure', it's understood that the
>operation to be performed on the object is the first argument to the closure.
>So there's really no difference at all if you take this view.

In article <1991Mar25.180147.5735@kestrel.edu> Gyro@Reasoning.COM writes:
>That's true.  But I don't take that view.  I prefer to think of
>operation dispatch as happening outside the object rather than inside
>it.

The problem with this is that it breaks encapsulation.  You don't want callers
to run arbitrary functions on your object.  The whole point of having methods
in a class definition is that those are the only functions which should be run
on instances of that class.

>Why?  Only because some, though certainly not all, object-oriented
>languages take the same view (C++, for instance [sorry]).

Actually C++ makes a good example of what i'm talking about, because it makes
some things explicit which other languages hide from you.  An object with
virtual methods contains a pointer to a virtual function table.  This table
has pointers to those functions which can be run on that object.

It is true that with C++ virtual methods the caller does a table lookup to
find the correct function to run.  But surely this is just an implementation
detail.  You could write a generic function which works on an object of any
actual class, by looking at this virtual table pointer and jumping to the
appropriate specific function.

But this would be silly, since the generic function is only the couple of
instructions to do the table lookup.  So it makes sense to inline it, and this
is exactly what C++ virtual methods are.  In C++ `virtual' on a method means
roughly `the caller does the table lookup'.

You can still have a non-virtual method which has different semantics
depending on the actual class.  For example, it can call other virtual
methods.  So really declaring a method virtual is only specifying which
implementation you want to use.

What about objects without any virtual methods?  There's not really any
conceptual difference, it's just that all the specific functions to be called
are known at compile time.  Therefore, there's no reason to store function
pointers in the object, it can just be pure data.  If the class has all
attributes protected, then this doesn't violate encapsulation in any way.
It's simply a different implementation technique.

Structs are a special cases of this, data with no encapsulated methods.
There's no protection on which functions can be called on a struct, besides
the usual type-checking.  We allow the user to access the attributes of the
object directly, and we assume knows what he's doing.
--
Joe Keane, functional C++ programmer