Keith Rarick

The Wormhole

I’m working on an experimental programming language called Sodium. Although I haven’t introduced the language yet, I must share a wonderful idea that recently occurred to me. These examples are written in Sodium, but I’ve avoided some unusual idioms; hopefully they will make sense to anyone with some programming background.

If I do a CPS transform, I can easily do exception-passing with continuations, exactly like return values. Every expression will have two continuations: success and failure (corresponding to return and raise). This implies call-with-current-continuations, plural. And that means I can write the following mind-blowing function:

def (wormhole promise):
  call/ccs (fn (success failure)
              (promise.on-success success)
              (promise.on-failure failure)

(Here, escape is just another continuation, saved at the beginning of the process, that pops back out to the main event loop.)

What does wormhole do? Consider the following snippet of typical asynchronous I/O code:

site = ""
path = "/robots.txt"
promise = (http.get site path)
promise.on-success (fn (robots-txt)
                      (spider site robots-txt))
promise.on-failure (fn (error)
                      (spider-all site))

This sort of thing can get pretty deeply nested and ugly, especially if you need to use lots of local variables.

Using wormhole, you can rewrite it:

site = ""
path = "/robots.txt"
  robots-txt = (wormhole (http.get site path))
  spider site robots-txt
catch error:
  spider-all site

This new example is fully asynchronous, just like the first one, but it is much more readable (as long as you know what’s really going on under the hood).

The wormhole operator has pretty serious implications for any callers of your code who expect things to happen in the usual synchronous order. It’s not clear if the resulting code is really any better, on balance.

Certainly, anyone tempted to use this device should think hard first. At least until we understand it better and develop rules of thumb. I think this function should be discouraged for library code, but, in the right circumstances, it might do for top-level application code. The end result can be simple and pretty while still fully asynchronous.