Background
The core of Clojure does not support top-notch sequels. This and the fact that the JVM does not provide a way to capture the current continuation, means that there is no way to implement letcc that is satisfactory for all situations.
However, in some situations, you can implement the continuation. In particular, if you have all the code (that is, the code in which you must write the continuations), you can use the continuation-passing style (CPS). Basically, you add an additional parameter for each function. This parameter is a function that represents a continuation of this call. You "return" the value by calling the continue function. Of course, this style is a pain to write on its own - but, fortunately, it is a transformation that we can easily apply to specific code using macros.
CPS itself is not suitable for platforms that do not perform tail call optimization (TCO). Since the last step of any function in CPS is to call another function, without TCO, the stack overflows quickly, with the exception of the most trivial calculations. This problem can be solved by using thunking and trampolining.
Decision
As I mentioned above, you can write your own CPS conversion using macros. However, I invite you to check out my pulley.cps library, which already does this for you. There are alternatives, but as far as I know, pulley.cps is the only Clojure library that provides all of the following features:
call-cc / let-cc- Seamless calls between native (not converted) and converted code
- Exception (
try / catch / finally ) support binding (they are also correctly recursive!)- Allows you to provide a CPS version of an existing native function (this is necessary if you want to keep the continuation in this function)
Alternatives include:
- delimc provides a library for delimited sequels. This does not seem very complete (for example,
binding fails because it does not understand the try / finally block) and was not affected after 4 years. - algo.monads is a monad library for Clojure. There is a strong and interesting connection between monads and sequels, and algo.monads is a continuation of the monad. Although the monadic style is not entirely clear, it has the advantage of making the effect more explicit, which can help in encapsulating code that uses control effects from code that does not. In addition, the
do notation (for example, the domonad macro) greatly blurs the lines between the direct and monadic styles.
source share