Multiprompt delimited continuations

Jonathan Brachthäuser jonathan.brachthaeuser at uni-tuebingen.de
Fri Sep 7 14:33:56 UTC 2018


Hey Ron,

thanks for your fast response.

Regarding recursion: While I couldn't immediately apply your trampolining trick,
your answer prompted me to look into it again and I found a potential solution
that works well for one-shot continuations. Maybe it is interesting for you:

https://gist.github.com/b-studios/65e9e614f006d2125659267f6113efac#file-delimcc-java-L28-L82

The basic idea is to install a second delimiter around the body of `shift` that allows to capture
the continuation after the call to `resume` -- up to the call of `shift`. The continuation is stored
in a java.lang.Stack which is later trampolined when returning from the original `reset`. That is,
for

    reset {
       ... shift { k => ... resume() ... }2 
    }1

we have a continuation delimited by block (1) and a continuation delimited by block (2). The first
one is suspended by `shift`, the second one is suspended by `resume`.

This solution only works if the continuation (passed as argument to `shift`) does not escape the
dynamic scope of `shift`. If it does, however, we still could fall back to the original implementation.

Again, thanks for making me look again into trampolining. 

Cheers

Jonathan

> On 6. Sep 2018, at 17:30, Ron Pressler <ron.pressler at oracle.com> wrote:
> 
> Hi Jonathan. 
> Glad to hear you like this project.
> 
> Regarding the recursion issue, this is the general problem of not having tail-
> calls, which this project aims to address once fibers are further along. In the
> meantime, you can trampoline (how it's done in Quasar: https://github.com/punive
> rse/quasar/blob/b80b66d871826c0794e7dcc10a9e245889f9f06f/quasar-
> core/src/main/java/co/paralleluniverse/fibers/Continuation.java#L235).
> 
> As to naming, the nomenclature is far from standardized when it comes to
> continuations. Obviously, Continuation is short for delimited continuation. Loom
> currently implements what can be called "non-reentrant (one-shot) assymetric
> delimited continuations with multiple named prompts," but with cloning, you get
> reentrant ("multi-shot") delimited continuations. You are correct that non-
> reentrant delimited continuations are often called coroutines, but the name
> coroutine also used to have the connotation of being symmetric, and more
> recently has gained the connotation (not in academic literature but in language
> implementations) of being a syntactic construct, rather than a purely dynamic
> one. In any event, what "coroutine" means exactly is not standardized, and "non-
> reentrant assymetric delimited continuations with multiple named prompts" is
> too-long a name. "Continuation" is sufficiently general and sufficiently vague
> to be malleable and applicable. Whatever we call this construct, if we decide to
> expose it as a public API, it will serve as a low-level construct, which the
> vast majority of programmers will only use indirectly through higher-level
> constructs such as fibers and generators. Having said that, we may decide to
> change the name to coroutine if we think it makes more sense or if we want to
> use the name continuation for something else.
> 
> 
> Ron
> 
> 
> On September 6, 2018 at 3:09:31 PM, Jonathan Brachthäuser (jonathan.brachthaeuser at uni-tuebingen.de) wrote:
> 
>> Hey everybody, 
>> 
>> first of all I want to say that I am really happy to see multiprompt  
>> delimited continuations coming to the JVM. Thanks to all committers and 
>> to Ron for all the great work! I experimented with the prototype and it 
>> already works great. 
>> 
>> While I see that the currently implemented API and programming model 
>> works perfectly well with Fibers (which is your main motivation as I 
>> understand), there are some issues that I would like to bring up. 
>> 
>> 
>> # Continuations in Literature 
>> Usually in literature "the continuation" represents the remainder of 
>> a program. To briefly summarize some background, for instance, in  
>> 
>> { 
>> 1 + callcc(k -> ... k.resume(3) ... ); 
>> System.out.println("Hello"); 
>> } 
>> 
>> `k` is the (undelimited) continuation. It expects a value of type `int`  
>> and represents the complete callstack at the point of calling `callcc` 
>> -- including both the addition `1 + []` and the `println` statement. 
>> 
>> On the other hand, with some notion of *delimited continuations*, we might have 
>> 
>> reset(p1, () -> 5 - shift(p1, k -> k.resume(2) * 7)); 
>> 
>> which evaluates to `(5 - 3) * 7 = 21`. The continuation `k` corresponds  
>> to the delimited evaluation context `5 - []`. 
>> 
>> 
>> # Continuations in Loom 
>> Since designed with fibers and threads in mind, continuations in loom  
>> conflate multiple aspects of delimited continuations in one class: 
>> 
>> - delimiting a continuation (`reset`). This is modeled by providing a 
>> runnable to the constructor, which can also be thought of as the 
>> initial continuation. 
>> - capturing the continuation (`shift`). While capturing the continuation 
>> is triggered by `Continuation.yield`, the actual continuation itself is 
>> never really exposed. Instead, an object of type `Continuation` in  
>> loom can be thought of as a **mutable reference cell** storing  
>> the actual current (delimited) continuation. 
>> - resuming a continuation (`resume`). When calling `Continuation.run`,  
>> the current continuation, stored in the mutable reference cell, is  
>> resumed and the cell is updated on yielding. 
>> 
>> I am bringing this up for two reasons: 
>> 
>> 1) Conceptually continuations, as they are currently proposed, are closer 
>> to coroutines than to continuations in the traditional sense. I don't 
>> want to start bike shedding, but naming them "Continuation" can be 
>> very confusing for people coming from a functional programming background. 
>> 
>> 2) The traditional interface of delimited continuations can be 
>> implemented using the current proposal: 
>> 
>> https://gist.github.com/b-studios/65e9e614f006d2125659267f6113efac#file-delimcc-java-L33-L37 
>> 
>> However, in order to actually run a continuation to the end a recursive  
>> function `runCont` is required and I couldn't yet find a non-recursive  
>> implementation. This obviously will lead to unnecessary stack  
>> usage. I am working on a library to program with algebraic effect 
>> handlers in Java, which is based on multiprompt delimited continuations. 
>> Project loom is very promising as a backend. However, the stack  
>> usage of `runCont`, imposed by the current loom design is a blocker 
>> since every yield will lead to an additional stackframe. Every 
>> long running computation will thus eventually run out of stack space. 
>> 
>> I would love to hear your thoughts on this. 
>> 
>> Again, thanks to everybody working on this project and all the best, 
>> 
>> Jonathan



More information about the loom-dev mailing list