quick sketch of composable continuation implementation

I've been told by knowledgeable people that it is impossible to
implement composable continuations (AKA delimited continuations AKA
shift/reset) in terms of call-with-current-continuation.  Since I
don't yet understand why that is, I figured it would help my
understanding to attempt it and see how it fails.
This commit is contained in:
Joel Dice 2013-12-14 20:06:51 -07:00
parent c5012cda72
commit 91e4d2b4a1
5 changed files with 155 additions and 0 deletions

View File

@ -120,6 +120,8 @@ import java.util.concurrent.Callable;
public class Continuations {
private Continuations() { }
private static final ThreadLocal<Reset> latestReset = new ThreadLocal();
/**
* Captures the current continuation, passing a reference to the
* specified receiver.
@ -181,6 +183,76 @@ public class Continuations {
}
}
public static <T> T reset(final Callable<T> thunk) throws Exception {
final Reset reset = new Reset(latestReset.get());
latestReset.set(reset);
try {
T result = callWithCurrentContinuation
(new CallbackReceiver<T>() {
public T receive(Callback<T> continuation) throws Exception {
reset.continuation = continuation;
return thunk.call();
}
});
if (reset.continuation != null) {
reset.continuation.handleResult(result);
}
return result;
} finally {
latestReset.set(reset.next);
}
}
public static <Result, Argument> Result shift
(final FunctionReceiver<Result, Argument> receiver)
throws Exception
{
return (Result) callWithCurrentContinuation(new CallbackReceiver() {
public Object receive(final Callback continuation) {
final Reset reset = latestReset.get();
final Callback resetContinuation = reset.continuation;
reset.continuation = new Callback() {
public void handleResult(Object ignored) {
try {
resetContinuation.handleResult
(receiver.receive
(new Function() {
public Object call(final Object argument)
throws Exception {
Object result = callWithCurrentContinuation
(new CallbackReceiver() {
public Object receive
(Callback shiftContinuation)
throws Exception
{
reset.continuation = shiftContinuation;
continuation.handleResult(argument);
throw new AssertionError();
}
});
reset.continuation = null;
return result;
}
}));
} catch (Exception e) {
resetContinuation.handleException(e);
}
}
public void handleException(Throwable exception) {
throw new AssertionError();
}
};
resetContinuation.handleResult(null);
throw new AssertionError();
}
});
}
private static native UnwindResult dynamicWind2(Runnable before,
Callable thunk,
Runnable after)
@ -235,4 +307,13 @@ public class Continuations {
this.exception = exception;
}
}
private static class Reset {
public Callback continuation;
public final Reset next;
public Reset(Reset next) {
this.next = next;
}
}
}

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2008-2013, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package avian;
public interface Function<Argument, Result> {
public Result call(Argument argument) throws Exception;
}

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2008-2013, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package avian;
public interface FunctionReceiver<Argument, Result> {
public Result receive(Function<Argument, Result> function) throws Exception;
}

View File

@ -1397,6 +1397,7 @@ unittest-depends = \
ifeq ($(continuations),true)
continuation-tests = \
extra.ComposableContinuations \
extra.Continuations \
extra.Coroutines \
extra.DynamicWind

View File

@ -0,0 +1,43 @@
package extra;
import static avian.Continuations.shift;
import static avian.Continuations.reset;
import avian.FunctionReceiver;
import avian.Function;
import java.util.concurrent.Callable;
public class ComposableContinuations {
private static void expect(boolean v) {
if (! v) throw new RuntimeException();
}
public static void main(String[] args) throws Exception {
expect(2 * reset(new Callable<Integer>() {
public Integer call() throws Exception {
return 1 + shift(new FunctionReceiver<Integer, Integer>() {
public Integer receive
(Function<Integer, Integer> continuation)
throws Exception
{
return continuation.call(5);
}
});
}
}) == 12);
expect(1 + reset(new Callable<Integer>() {
public Integer call() throws Exception {
return 2 * shift(new FunctionReceiver<Integer, Integer>() {
public Integer receive
(Function<Integer, Integer> continuation)
throws Exception
{
return continuation.call(continuation.call(4));
}
});
}
}) == 17);
}
}