|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
public interface Continuation<T>
A thunk enabling iterative evaluation of a recursive function. If a function with
a continuation return type can immediately produce the result value, it wraps the value
in a simple continuation; otherwise, it wraps a recursive computation in a continuation
that, when resolved, will produce the result. To produce a value from a continuation, clients
may either invoke value() or iteratively invoke step() until isResolved()
on the result is true.
Traditionally, continuations are functions that are threaded through all recursive
invocations of some function. Such an approach allows continuation-based functions to
be defined exclusively in terms of tail recursion, and to immediately return a result.
Here, in contrast, we use continuations to solve two problems: first, as
traditionally, they allow us to represent calling contexts without relying on the stack;
and second, they delay the evaluation of recursion so that even tail recursion is prevented
from filling up the stack (since Java does not perform tail-call optimization).
Continuation-based methods thus must return, where T is the original return type,
a Continuation<T> rather than a T. To prevent unnecessary clutter,
these methods are not required to accept a continuation as a parameter; instead,
they may assume that there is nothing more to be done with the result, and where this
is not the case, the continuation classes' implementation of step() will handle
the result appropriately.
As an example, here are two recursive functions translated to use Continuations.
First, a tail-recursive method:
boolean isEven(int x) {
if (x == 0) { return true; }
if (x == 1) { return false; }
else { return isEven(x - 2); }
}
This can be written using continuations as follows:
Continuation<Boolean> isEven(int x) {
if (x == 0) { return ValueContinuation.make(true); }
if (x == 1) { return ValueContinuation.make(false); }
else {
return new PendingContinuation<Boolean>() {
public Continuation<? extends Boolean> step() {
return isEven(x - 2);
}
};
}
}
Second, a recursive method that requires on calling context:
long sum(int n) {
if (n == 0) { return 0l; }
else { return sum(n-1) + n; }
}
This is written with continuations thus:
Continuation<Long> sum(final int n) {
if (n == 0l) { return ValueContinuation.make(0l); }
else {
return new ArgContinuation<Long, Long>() {
public Continuation<Long> arg() { return sum(n-1); }
public Continuation<Long> apply(Long arg) {
return ValueContinuation.make(arg + n);
}
};
}
}
In both cases, evaluation of the original function with large inputs will lead to a stack overflow,
while the stack size in the continuation-based versions is bounded by a small constant.
As an illustration, an invocation of sum(3), as defined above, results in the following
evaluation (using informal abbreviations to represent the continuations and lambdas involved):
sum(3) = Arg(sum(2), \x.Val(x+3)) [STEP] Comp(Arg(sum(1), \x.Val(x+2)), \x.Val(x+3)) [STEP] Comp(Arg(sum(0), \x.Val(x+1)), \x.Val(x+2)).compose(\x.Val(x+3)) = Comp(Arg(sum(0), \x.Val(x+1)), \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) [STEP] Comp(Val(0), \x.Val(x+1)).compose( \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) = Comp(Val(0), \z.(\x.Val(x+1))(z).compose( \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) ) [STEP] (\x.Val(x+1))(0).compose( \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) = Val(1).compose( \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) = Comp(Val(1), \y.(\x.Val(x+2))(y).compose(\x.Val(x+3)) ) [STEP] \x.Val(x+2))(1).compose(\x.Val(x+3)) = Val(3).compose(\x.Val(x+3)) = Comp(Val(3), \x.Val(x+3)) [STEP] Val(6)There are 6 steps: three to expand the recursion to the base case, and three to perform the necessary computation after a recursive result has been determined.
| Method Summary | ||
|---|---|---|
|
compose(Lambda<? super T,? extends Continuation<? extends R>> c)
Produce a continuation that will invoke c with this object's result. |
|
boolean |
isResolved()
Return true iff the continuation has been resolved to a value. |
|
Continuation<? extends T> |
step()
Produce a continuation representing the next step of computation. |
|
T |
value()
Iteratively resolve the continuation to a value. |
|
| Method Detail |
|---|
T value()
value in interface Thunk<T>boolean isResolved()
true iff the continuation has been resolved to a value.
isResolved in interface ResolvingThunk<T>Continuation<? extends T> step()
java.lang.IllegalStateException - If isResolved() is true<R> Continuation<? extends R> compose(Lambda<? super T,? extends Continuation<? extends R>> c)
c with this object's result.
|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||