Implementing Smalltalk’s Non-Local Returns in JavaScript

February 2, 2011

In Smalltalk, there are special rules for the “return” statement that assist in control structures. And often these have caused porting problems to languages that lack either a “go to” statement or the ability to manipulate the execution stack.

An example will help illustrate the issue:

Try this is Squeak:

Transcript show: ‘in home context 1′;cr.
[Transcript show: 'in inner block context';cr.] value.
Transcript show: ‘in home context 2′;cr.
==>
‘in home context 1′
‘in inner block context’
‘in home context 2′

and then put a return statement in the inner block:

Transcript show: ‘in home context 1′;cr.
[Transcript show: 'in inner block context';cr. ^nil] value.
Transcript show: ‘in home context 2′;cr.
==>
‘in home context 1′
‘in inner block context’

As you can see the “return” in the block acted as though it was in the “home” (outer) context and caused the entire method to exit.

Let’s do the same thing in JavaScript and see what happens: (example for Chrome Console)

(function() {
console.log(‘in home context 1′);
(function() {console.log(‘in inner block context’);})();
console.log(‘in home context 2′);
})()
==>
‘in home context 1′
‘in inner block context’
‘in home context 2′

It works the same way as Smalltalk.
Now put in a return statement in the inner block (function):

(function() {
console.log(‘in home context 1′);
(function() {console.log(‘in inner block context’); return null;})();
console.log(‘in home context 2′);
})()
==>
‘in home context 1′
‘in inner block context’
‘in home context 2′

The “return” in the inner block didn’t have any effect.

So let’s fix it:
Without a return:

(function() {
var context={};
console.log(‘in home context 1′);
var result = (function(context) {console.log(‘in inner block context’);})(context);
if (context.return)
return result;
console.log(‘in home context 2′);
})()
==>
‘in home context 1′
‘in inner block context’
‘in home context 2′

It works just like before.
Now put in the “return” statement:

(function() {
var context={};
console.log(‘in home context 1′);
var result = (function(context) {console.log(‘in inner block context’); context.return=true; return null;})(context);
if (context.return)
return result;
console.log(‘in home context 2′);
})()
==>
‘in home context 1′
‘in inner block context’

And it works just like Smalltalk does.

The cost of doing this is a few extra program statements and (maybe) a couple milliseconds of execution time.

But this shouldn’t matter at all to a Smalltalk developer because it is the compiler (QuickSilver or other) that will do the work automatically.

IMHO, it is the little details like this that have kept Smalltalk from being deployed everywhere on billions of client devices.

And few lines of code is all that it takes to fix the problem.

About these ads

12 Responses to “Implementing Smalltalk’s Non-Local Returns in JavaScript”

  1. Carl Gundel Says:

    That very nice. Are Javascript contexts full closures?

  2. Nicolas Petton Says:

    It seems to work fine for single block evaluation, but what about early returns in loops?

    • Peter Fisk Says:

      Hi Nicolas,

      The same context would be passed to every next block being called.
      If any of them set the return flag, it would propagate upwards to the home context.

      AFAICT, the strategy should work for loops and any number of nested calls.

      Perhaps someone could post an example where it would fail?

      — Peter


  3. How does the compiler help with the “(maybe) a couple milliseconds of execution time”. It can’t. I still have to execute that stuff all the time. The compiler just hides the drugdery of generating it.

    It would be interesting to “measure” this approach vs turning returns into thrown exceptions, and I guess other techniques (don’t know any). At least time you could measure time easily; Chrome’s devtools heap profiling may help with memory usage measurement.

    And where the heck is my debugger????!!!!!

    Would be cool to have a story where I can store source and compiled output in the browser (local storage) and then sync it to a server for my SCM needs. Then all the browser can operate locally, and separate sync browsers can arrange to keep everything in the cloud. Can you have this by next week?

    (great job, obviously!)

    • Peter Fisk Says:

      Hi Patrick,

      It would be great for any JavaScript gurus to suggest making the code faster.

      As for syncing to the server…

      The first script tag in the header of “index3.html” loads the JavaScript libraries Google’s Channel API (real time messaging using sockets).

      And its all set up to go on the server side too.

      Just be patient a few more days…

      — Peter


  4. I’d suggest you use a different approach. See https://mail.mozilla.org/pipermail/es-discuss/2011-February/012734.html

    This more closely models what actual Smalltalk VMs do.


    • The main problem with the “throw” approach, is that it kills the ability, in the debugger, of “stop on caught exception” to be used anymore. Now, I rarely use that, in practice, but still. So it seems to me, if it was possible to do this in multiple ways, I might want both. Peter’s first approach for development time, the “throw” approach for deployment time.


      • Well, Smalltalk-style resumable exceptions don’t have the same semantics as JavaScript exceptions. To faithfully implemented the Smalltalk exception semantics you are probably going to need to do similar tricks using JS exceptions to implement non-local control transfers. That will also impact use of the debugger.

        Debugging is one of the downsides of layered language implementations like this. Using a JS debugger to debug hosted Smalltalk code is the moral equivalent of using an assembly language debugger to debug c++ code. Possible but not pleasant. You really want a Smalltalk specific debugger.

        The ECMAScript standards committee has a goal of making sure that JS can be a good compilation target for hosted languages. But to complete the picture JS implementers will need to make sure that their tools/tool API are powerful enough to create good dev. experiences for such hosted languages.

      • pmuellr Says:

        “Using a JS debugger to debug hosted Smalltalk code is the moral equivalent of using an assembly language debugger to debug c++ code. Possible but not pleasant. You really want a Smalltalk specific debugger.”

        Yes, I’ve felt this pain.

        But I half expect to be able to write some libraries in Smalltalk, that I could use somehow with plain old JS code. I’d expect the Smalltalk code to just work, much as I expect some particular version of jQuery to work, and not have to worry too much about it being debuggable (ie, it might be min-ized).

        In any case, having alternative implementations would probably be a good thing to do.

        And I didn’t mean to venture down the path of Smalltalk exceptions – might be best to ignore that subject for a bit :-)

  5. Peter Fisk Says:

    Allen, thank you for the insight.

    My approach is to build the first thing that works reasonably and then let it evolve as developers implement real-world tools and apps.

    I don’t know what the *best* solution is going to be; maybe we can try some different versions and see what survives.

    BTW, I much enjoyed your 2008 video with Dan Ingalls.

    — Peter


  6. [...] Peter Fisk (VistaSmalltak, GWT Smalltalk, and now SilverSmalltalk) was written Smalltalk using Silverlight, Google tools, and now, Javascript: Implementing Smalltalk’s Non-Local Returns in JavaScript « Silver Smalltalk http://silversmalltalk.wordpress.com/2011/02/02/implementing-smalltalks-non-local-returns-in-javascr… [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: