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