[self-interest] Reason for closure/method discrepancy?

Jecel Assumpcao Jr jecel at merlintec.com
Wed Dec 19 19:29:54 UTC 2001


Victor,

> Can anyone justify closure treatment in self?  It would seem to be
> more in the spirit of self to merge closures with methods and perform
> a purely dynamic message lookup, that is search block
> locals/parameters, then follow parent slots (outer closures - and now
> deprecated 'methods'). 

At the language definition level (the implementation is an entirely 
different story) things are pretty close to what you proposed. But 
methods are considered prototypes which must be cloned in order to get 
a closure.

At least in theory, a prototype and its clone would be considered the 
same type or class of object. So it would be correct to say that 
methods and closures are already merged.

To see why method invocation must generate a clone, just imagine a 
recursive method or one used in several parallel threads. The different 
execution contexts would want to store different values in the argument 
slots and local slots, so we can't get by with just one.

> This would have an interesting side effect of
> erasing the difference between closures, methods and objects - any of
> these could be stuck into a slot and sent at least a 'value' message.

Would fetching something from a slot automatically involve sending 
'value' to what was found there? If so, blocks would no longer work 
properly (more below). If not, methods would no longer work properly.

> A method implementation would become therefore just another object
> (with local, parameter and parent slots) which is sent a 'value'
> message by default, but can accept other messages!

A closure (context object, activation, whatever name you want) can 
already accept many messages, though the current implementation doesn't 
deal with method slots inside methods.

>   Then it is up to the programmer to decide whether to write
> a 'block' inside a 'method' (an anonymous closure), create a method
> (a named closure) or send messages to object or methods (that are
> also objects) interchangeably.

Blocks turn out to be much more complicated than they appear at first. 
In fact, we need three separate objects to support them:

   block literal: this bundles up all the information in source and 
gets stored in the method object

   block context: this is the name I gave this in my tinySelf 
interpreter, but it really isn't a context object at all. It must point 
to the block literal for later use, point to the currently executing 
context for later use, and have 'traits block' as a parent so we can 
send all kinds of interesting messages to it ('value' is the most 
important, but we need 'whileTrue:', 'millisecondsToRun' and many 
others)

   block method context: this is created as the result of sending 
'value' to the block context. It is cloned from the method found in the 
block literal but its parent slot is set to point to the context saved 
in the block context instead of the receiver like for normal methods. 
This way we have a chain of closures like you wanted.

For a single block literal we might create several block contexts since 
the method in which it appears might be invoked multiple times. For a 
single block context we might create seveal block method contexts since 
it might receive several 'value' messages.

>   As for the non-local return out of closures, is there a really good
> reason for it?  Why not return to the outer scope (other than perhaps
> a clumsier syntax)?

Unless you have a "^" before the last expression in a block, it does 
return to the outer scope.

              [ 1 + 2. 3 + 4 ] value

But that is not always what you want. Sometimes you need to "unwind" a 
deeply nested computation:

             ....
             arg < 0 ifTrue: [ ^ 1 ].
             ....

This lets us relax the rules of strict structured programming. 
Otherwise we would have to do:

            ....
            arg < 0 ifTrue: [ 1 ]
                        False: [
                ....]

For many real programs, being unable to escape inner scopes in an 
unstructured way can become quite painful (see Pascal vs C).

>   Forgive my possible stupidity - I am stuck in x86 world and haven't 
> been able to explore self personally.  I am amazed at the clarity of 
> reasoning behind self, with this minor exception.  Perhaps someone 
> can clarify this.

These are very good questions and I hope I understood them correctly 
and was able to explain some of the issues involved. I have been trying 
to simplify blocks or unify them with methods since my 1984 Smalltalk 
design (http://www.lsi.usp.br/~jecel/st84.txt) and would very much like 
to see a simpler solution for Self.

-- Jecel



More information about the Self-interest mailing list