I read lots of article already on how to translate call/cc into equivalent CPS style and kinda know the basic already but currently I don't understand how racket perform transformation with call/comp (a.k.a call-with-composable-continuation) and stuck here.
What I did first is attempt to translate this racket piece of code:
#lang racket/base
(let ([tag (make-continuation-prompt-tag)])
(println "111")
(println (string-append "222"
(call-with-continuation-prompt
(lambda ()
(println (string-append "333"
(call-with-composable-continuation
(lambda (k)
(println (string-append "444" (k "555")))
;; (abort-current-continuation tag "bbb")
"666")
tag)))
"777")
tag
(lambda (k)
(println (string-append "888" k))
"aaa"
)))))
Into equivalent JS code. Here my current attempt so far:
var prompt = (fn, tag, r0) => { // call-with-continuation-prompt
// Incorrect
// r0(fn((p0) => {
// r0(p0);
// }));
};
var comp = (fn, tag, r0) => { // call-with-composable-continuation
// Incorrect
// fn((pass, r1) => {
// r0(r1(pass));
// });
};
var abort = () => { // abort-current-continuation
};
((r0) => {
console.log("111");
((r1) => {
prompt((r2) => {
((r3)=>{
comp((k, r4) => {
k("555", (r5) => {
console.log("444" + r5);
r4("666");
});
}, "tag", r3);
})
((p3) => {
console.log("333" + p3);
r2("777");
});
}, "tag", r1);
})
((p1) => {
console.log("222" + p1);
r0();
});
})
((p0) => {
throw 0; // throw 0 to exit early
});
How should I fill prompt and comp here? Did I missed a CPS step? Is the transformation even possible?
After a while with a lots of trial and error I came up with this solution:
var give = (p, rs, rt) => { // return value from function
rs(p, rt);
};
var exit = (p, rr) => { // exit program?
give(p, rr, rr);
};
var prompt = (fn, r_ab, r_cx) => {
fn(
(p_fn, r_fn) => {
if (r_fn == null) // Check if we are aborting
r_ab(p_fn, (p_ab, r_ab_) => {
give(p_ab, r_ab_, r_cx);
});
else
give(p_fn, r_fn, r_cx);
}
);
};
var comp = (fn, r_cx) => {
fn(
(p_fn, r_fn) => { // Function composition
r_cx(p_fn, (p_cc) => {
give(p_cc, r_fn, (p_cx) => {
r_cx(p_cx, (p_out, r_out) => {
exit(p_out, r_out);
});
});
});
},
(p_fn, r_fn) => {
give(p_fn, r_fn, r_cx);
},
);
};
var abort = (fn, rr) => {
fn((p_fn) => {
rr(p_fn, null);
});
};
((r0) => {
console.log("111");
prompt(
(r1) => {
comp(
(k, r2) => {
// return give("666", r2, r1); // Or comment k(...) and uncomment this
k(
"555",
(pk, rk_) => {
console.log("444" + pk);
/* <-- Flip between these two
abort(
(ra) => {
ra("bbb");
},
r1
);
/*/
give("666", rk_, r2);
//*/
},
);
},
(p2, r2_) => {
console.log("333" + p2);
give("777", r2_, r1);
},
);
},
(pa, ra_) => {
console.log("888" + pa);
exit("aaa", ra_);
},
(p1, r1_) => {
console.log("222" + p1);
exit(0, r0); // Should end the program
},
);
})
((p0, r0_) => {
give(p0, r0_, (p0_) => {
console.log("out: ", p0_);
});
});
Honestly I'm still not sure if my implementation is correct for all possible cases with valid CPS. Any thought?