[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon May 31 11:47:06 2001 
Subject:Closures and OOP (Was Re: filenames, argument list separators + gsl extension) 
From:Aaron Sloman See text for reply address 
Volume-ID:1010531.01 

[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")=>
    ** <key word>

    datakey(hd)=>
    ** <key procedure>

    datakey(sqrt(-1))=>
    ** <key complex>

    datakey(-1)=>
    ** <key integer>

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