Two articles posted to comp.lang.pop on how objectlass instances could be used to implement the same sort of thing as lexical closures and partial application in pop-11. ======================================================================= Newsgroups: comp.lang.pop Message-ID: <3b01019e.257882975@News.CIS.DFN.DE> References: <9dpkp0$95a$1@ctb-nnrp2.saix.net> Xref: bhamcs comp.lang.pop:2772 Date: Tue, 15 May 2001 11:01:28 GMT Subject: Re: filenames, argument list separators + gsl extension From: Jonathan.Cunningham@tesco.net (Jonathan L Cunningham) On Mon, 14 May 2001 22:50:29 GMT, hedgehog@electric-hedgehog.net (Chris Dollin) wrote: >Part of that is syntax; part of it is the Poplog VM. The latter controls >what kind of things can be said; the former controls how *easily* they >may be said. For example, Pop's syntax for closures is pretty easy to >write and read; it gets used a *lot*. It's not perfect (I think Spice >"holes" will turn out better, but they've not been used in anger), but >it's easier than explicit lambda-expressions. If it's clumsy to write, >*it doesn't get used*. Pop's closures (partial application) are very useful, and much more convenient than lambda-expressions as in Lisp. There was a discussion on this group a few years back (probably even before LIB objectclass) about the advantages of object-oriented programming. Somehow the question got raised about how you would get the same effect as pop closures in an OO language. It can be done, without using anything like closures or lambda expressions. And it's messy: you need to define a new class. As an interesting corollary, one of the few "big" changes to the syntax of Java, after its first release, was the provision of a mechanism for defining anonymous classes. (In the same way that the pop11 "procedure" syntax lets you define anonymous procedures.) IMO, one main purpose of anonymous classes was precisely those cases where, in pop, we would use closures. Most of the Java effort has gone into providing libraries of higher level constructs, such as java beans, with very few changes to the syntax of the language. This is, of course, in line with Aaron's comments, and what we would expect in pop. But should we, for example, want to add the concept of anonymous classes to lib objectclass? Pop11 is so rich, syntactically, that I don't suppose anyone has felt any need for them, but going one step further than Chris's remark above: if it ain't in the language *it sure as hell doesn't get used*. OK, I shouldn't be spending time on this, but to provide a few examples, suppose we have a function called "triple" as follows: define triple(x, y, z); [first ^x second ^y third ^z]=> enddefine; It is fairly obvious what this does: triple("one", "two", "three"); ** [first one second two third three] And closures can freeze in the last argument(s), as in triple(% "jump" %) -> double; so that double("hop", "skip"); ** [first hop second skip third jump] Suppose we now want to freeze in the middle argument instead? This is where lambda expressions, or pop procedures, are more powerful but syntactically clumsier. This will freeze in the second argument of a three argument function: define midclosure(f, arg); procedure x, y; f(x, arg, y); endprocedure enddefine; (It's actually a bit more general than that, but let's not get too pedantic here.) We can use it as follows: midclosure(triple, "beta") -> doublet; doublet("alpha", "gamma"); ** [first alpha second beta third gamma] doublet("bet", "best"); ** [first bet second beta third best] Ok, how would you do this in a language which doesn't have procedures as objects which can be manipulated in this way? In an object oriented language, you can pass around objects instead. Using objectclass (which I'm not very familiar with, so maybe this can be cleaned up), you will need an object which will represent the three arg function "triple". (Remember, from now on, we are pretending that we can't pass around functions are arguments to other functions.) First we declare the class of the object for the triple function: define :class three_arg_func; enddefine; There is nothing in it, because we don't really need a whole new class. It is only there so that we can create an object. The only thing about this object is that it represents a three argument function, so let's declare that next: define :method apply_three_arg(f:three_arg_func, x, y, z); [first ^x second ^y third ^z]=> enddefine; This is our old friend "triple" -- or rather, it is the declaration of what triple will do. We still have to create the actual object: consthree_arg_func() -> triple; Now we can use the object like a function: apply_three_arg(triple, "alpha", "beta", "gamma"); ** [first alpha second beta third gamma] Now we are in a position to create the equivalent of a closure, or lambda expression. We will need to inherit the apply_three_arg function, so we will need a new class: define :class midclosure is three_arg_func; slot middlearg; enddefine; This new class will represent a two argument function: define :method apply_two_arg(f:midclosure, x, z); apply_three_arg(f, x, middlearg(f), z); enddefine; and lastly, having defined the behaviour of our new object, we need to actually create it: instance midclosure middlearg = "psi" endinstance -> doublet; and we can use this in a similar way: apply_two_arg(doublet, "chi", "omega"); ** [first chi second psi third omega] If you put all that example code together you will agree, I think, that when pop closures can be used, they certainly encapsulate a lot of functionality!! But don't, for one minute, think they are *simpler*. All that the objectclass stuff is doing, is making explicit, and bringing to the surface, something very similar to what is actually going on inside the poplog virtual machine for pop closures. The difference is that the "class" of pop functions is already implicit in the language, as is the "class" of closures. There are some other ways in which the objectclass stuff looks ugly because of syntactic restrictions. For example, in C++ I could have used the same name "apply" for both the apply methods, instead of the clumsier "apply_two_arg" and "apply_three_arg". In Java, I needn't have declared the class "midclosure" -- I could have used an anonymous class. And so on. But the bottom line is that an apparently "simple" mechanism -- the pop closure -- is actually a very powerful abstraction, which hides a lot of behind-the-scenes complexity. And that is part of what makes it so useful. Jonathan -- Jonathan L Cunningham ======================================================================= Newsgroups: comp.lang.pop References: <9dpkp0$95a$1@ctb-nnrp2.saix.net> <3b01019e.257882975@News.CIS.DFN.DE> Subject: Closures and OOP (Was Re: filenames, argument list separators + gsl extension) Date: Thu May 31 12:48:47 BST 2001 From: Aaron.Sloman.XX@cs.bham.ac.uk (Aaron Sloman See text for reply address) [To reply replace "Aaron.Sloman.XX" with "A.Sloman"] Jonathan.Cunningham@tesco.net (Jonathan L Cunningham) wrote an interesting message about closures and objectclass instances > Date: Tue, 15 May 2001 11:01:28 GMT > ....(lots cut out) .... > But the bottom line is that an apparently "simple" mechanism -- > the pop closure -- is actually a very powerful abstraction, > which hides a lot of behind-the-scenes complexity. And that > is part of what makes it so useful. Jonathan, I liked your tutorial on closures and objectclass. I have temporarily saved it in http://www.cs.bham.ac.uk/~axs/misc/closures.and.classes.txt to which I'll append this message. One day I'll turn it into a TEACH file on closures and class instances. If and when I get round to doing this, I'll let you have a chance to edit it! A few small points. You wrote: > There are some other ways in which the objectclass stuff > looks ugly because of syntactic restrictions. For example, > in C++ I could have used the same name "apply" for both > the apply methods, instead of the clumsier "apply_two_arg" > and "apply_three_arg". This could be handled by using class_apply. From REF KEYS: | class_apply(key) -> apply_p [procedure] | apply_p -> class_apply(key) | .... | The updater of class_apply assigns the procedure apply_p to | be the apply procedure for the class key key. This means that instances can be treated as if they were procedures, and when they are applied to arguments the class_apply procedure for the Pop11 class to which they belong will be run. Every pop11 entity has a class represented by its datakey datakey("cat")=> ** datakey(hd)=> ** datakey(sqrt(-1))=> ** datakey(-1)=> ** which holds generic information about entities in that class, including what should be done if you try to treat that entity as a procedure, e.g. when applying a list to a number. [cat dog mouse](2)=> ** dog This is not to be confused with objectclass classes. See HELP CLASSES REF KEYS REF OBJECTCLASS says that the method apply_instance is the default class_apply for objeclass instances. So you can redefine it as a method that does different things for different classes. So with a little extra work (illustrated below) you can then replace > apply_two_arg(doublet, "chi", "omega"); with doublet("chi", "omega"); So unlike c++ you will not even need to use "apply" to run your objectclass-based closures! > In Java, I needn't have declared > the class "midclosure" -- I could have used an anonymous > class. And so on. I don't know how anonymous classes work in Java, but I guess there are at least three reasons for using anonymous classes: (a) privacy: the class and its methods can be accessed only in the scope of the text where the class is introduced. (b) efficiency because global identifiers don't need to be created (c) syntactic convenience In objectclass (a) and (b) can be achieved by using lvars or lconstant in the class definition, and using lblock endlblock to restrict the scope of the lexical identifiers. An example is given below. Maybe (c), the extra syntactic convenience, could (if desired) be achieved by defining new syntax words or macros to do the equivalent of all this. Anyhow here is an example illustrating the use of apply_instance, lconstant and lblock, in connection with your example (slightly simplified): Suppose we have a procedure for which we wish to create a closure that fixes the middle argument: define apply_three(x, y, z); [first ^x second ^y third ^z]=> enddefine; uses objectclass; ;;; start the lexical block lblock ;;; define our new "anonymous" class define :class lconstant midclosure; slot middlearg; enddefine; ;;; change the class_apply for its instances define :method apply_instance(f:midclosure); ;;; get two arguments from the stack lvars x, z; -> (x, z); apply_three(f, x, middlearg(f), z); enddefine; ;;; create an instance inside the lexical block define :instance doublet :midclosure; middlearg = "psi"; enddefine; endlblock; ;;; Then test that doublet works as expected, i.e. as a closure ;;; of apply_three doublet("chi", "omega"); ** [first chi second psi third omega] doublet("cat", "dog"); ** [first cat second psi third dog] Q.E.D. Compare this standard Pop-11 version which returns a closure of an anonymous procedure which runs apply_three: vars doublet2 = procedure(y); procedure(x, z); apply_three(x, y, z) endprocedure; endprocedure("psi"); doublet2("cat", "dog"); ** [first cat second psi third dog] or, simplest of all, using partial application vars doublet3 = procedure(x, z, y); apply_three(x, y, z) endprocedure(% "psi" %); doublet3("cat", "dog"); ** [first cat second psi third dog] Aaron ==== Aaron Sloman, ( http://www.cs.bham.ac.uk/~axs/ ) School of Computer Science, The University of Birmingham, B15 2TT, UK EMAIL A.Sloman AT cs.bham.ac.uk (ReadATas@please !) PAPERS: http://www.cs.bham.ac.uk/research/cogaff/ FREE TOOLS: http://www.cs.bham.ac.uk/research/poplog/freepoplog.html