Briefly, and probably mostly inaccurately, Continuation Passing Style (CPS) programming uses functions that pass their results to another function instead of more familiar (to me) functions that return values. That other function then "continues" the computation. CPS programming has been (derogatorily?) called "gotos with arguments". The Wikipedia article on continuations describes them as saving the executation state of a program for later resumption.
I wondered what continuations would look like in Java. To prepare, here's a brief summary of the example from Duchier's article.
Suppose we want to implement a function that computes 2*x+y using continuations. Further suppose that we have continuation-style multiplication and addition methods, with signatures
long add(long x,long y,Continuation c)and
long multiply(long x,long y,Continuation c)We'd define our new function (baz) as
long baz(long x,long y,Continuation c){
multiply(2,x,lambda z,y=y,c=c: add(z,y,c);
}There's some non-Java code in our invocation of the multiply method:
lambda z,y=y,c=c: add(z,y,c)The purpose of this code is to create a new anonymous function (a closure, but that's a whole other topic that I need to learn about) that accepts the result of the multiply, y and the "top-level" continuation and invokes the CPS-style add method. Another way to think about it is that we're creating a new continuation, initializing it with y a c and then invoking it with z (2*x).
So how can I implement the continuation in Java? I start with a Continuation interface:
public interface Continuation{
public void call(long l);
}
Then I create a helper Continuation class that dumps its results to stdout:
public class PrintContinuation implements Continuation{
public PrintContinuation(String name){
this.setName(name);
}
public void call(long x){
System.out.println(this.getName()+"="+x);
}
private String theName;
public void setName(String name){
this.theName=name;
}
public String getName(){
return this.theName;
}
}
Now I'm ready to create a poorly-name Functions class that contains the CPS add, multiply and baz functions.
public class Functions{
public void add(long x,long y,Continuation c){
c.call(x+y);
}
public void multiply(long x,long y,Continuation c){
c.call(x*y);
}
public void baz(long x,long y,Continuation c){
this.multiply(2,x,new AdderContinuation(y,c));
}
public static void main(String[] args){
Functions f=new Functions();
f.add(3,4,new PrintContinuation("3+4"));
f.multiply(5,6,new PrintContinuation("5*6"));
f.baz(7,8,new PrintContinuation("baz(7,8)"));
}
}
Note that
baz's implementation computes 2*x and passes the result to a Continuation that adds it (2*x) to y and passes the result (2*x+y) to the Continuation parameter. baz's implementation uses an up-until-now-undefined class called AdderContinuation:
public class AdderContinuation implements Continuation{
public AdderContinuation(long y,Continuation c){
this.setY(y);
this.setContinuation(c);
}
public void call(long x){
Functions f=new Functions();
f.add(x,this.getY(),this.getContinuation());
}
private long theY;
public void setY(long y){
this.theY=y;
}
public long getY(){
return this.theY;
}
private Continuation theContinuation;
public void setContinuation(Continuation
this.theContinuation=continuation;
}
public Continuation getContinuation(){
return this.theContinuation;
}
}
I think AdderContinuation is the most interesting class. It's a Continuation that you initialize with a long (
y) and a Continuation and that accepts a single parameter (x in call). When called, it invokes the CPS add method, passing in x, y and the Continuation. AdderContinuation is basically a Java implementation of the Python closure lambda z,y=y,c=c:add(z,y,c), in twenty-four lines! Using this architecture, I'd have to create [function]Continuation classes for every function I want to wrap in a Continuation. So, while I can implement continuations and closures in Java, there's a lot of boilerplate code. (Steve Yegge has some interesting and funny comments on this.)
No comments:
Post a Comment