While I completely agree with Urs and Peter, I also wanted to stick in my own 2 cent's worth.
Here's the way I look at it: Self does even less scheduling than Smalltalk in the VM. This costs you in more process switches, but may make it easier to play with the scheduler. Like Smalltalk, our standard libraries do not allow for concurrency. Unlike Smalltalk, you can easily change the fundamental system objects and just file them in, since our compiler/parser is "below the line". (Of course this makes it harder to change Self's parser.) I am very pleased by the minimalism in the Self VM's treatment of processes. I hope it works out.
By the way, on non-LIFO blocks: The whole issue of how far to reify activations and how much upward functionality to give blocks is a REAL toughie. The advantages are well known, but I think the costs are frequently underestimated. Specifically: performance. We would love Self to compete head-to-head with optimized C in runtime performance. I suspect that either reified activations or upward blocks would make this MUCH harder if not impossible. I do not believe that the work that the LISP community has done so far contradicts my view. For example, one LISP system that is considered pretty efficient (the subject of Kent Dybvig's SIGPLAN 90 paper on high-performance continuations) does not even use register windows on the SPARC--a MUST to compete with C on that machine. For regrettable, but good reasons, there is a dearth of head-to-head performance comparisons between C and LISP, and the ones we are aware of have used "shortcuts" like declarations or unsafe arithmetic functions. They do not give me any hope that, in the 1990's, we can approach optimized C performance AND do upward blocks in a language like Self. Sorry for the long answer, I hope to be proven wrong.
David
The only time you have to do anything special with a block is when someone assigns it to a non-temporary variable or returns it. If they don't do that, you can treat it as a just like you do now. I guess a non-LIFO block makes any regular blocks hanging around in its scope (i.e. assigned to temporary variables somewhere) into non-LIFO blocks.
Couldn't you modify self's gnarly inlining to handle this? I.e. wouldn't inlining be able to track a lot of cases when it is known that you don't assign a block to a non-temporary variable, return it, or have it returned for you (like from one of your blocks)? I think that blocks which fit these constraints don't get passed around too much, anyway, so they'd be easier to find. This might at least work for the standard control structures in self.
When a block is determined to be non-LIFO, you can just copy the scope of the block. Then, replace the variables in the stack frames with forwarders to the newly created variables (it'd be a bit more complex than that, because it gets hairier for cases of several non-LIFO blocks sharing scope, but that's the general idea). Alternatively, you could use dynamic inheritance: kill the data slots in the active methods up to (but not including) the top level environment and make the active methods inheriting children of their corresponding copies.
Either of these would invalidate the code for the currently active scope; you'd have to make a new map to change the variable references into message passing (in the compiled code) for the temporary variables in the methods. *BUT* this only happens when you want to use a non-LIFO block, and then it only affects the efficiency of the currently active method/block objects, not the prototypes for them.
-- Bill burdick@mentor.cc.purdue.edu
self-interest@lists.selflanguage.org